|
| 1 | +// Copyright 2021 Evmos Foundation |
| 2 | +// This file is part of Evmos' Ethermint library. |
| 3 | +// |
| 4 | +// The Ethermint library is free software: you can redistribute it and/or modify |
| 5 | +// it under the terms of the GNU Lesser General Public License as published by |
| 6 | +// the Free Software Foundation, either version 3 of the License, or |
| 7 | +// (at your option) any later version. |
| 8 | +// |
| 9 | +// The Ethermint library is distributed in the hope that it will be useful, |
| 10 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | +// GNU Lesser General Public License for more details. |
| 13 | +// |
| 14 | +// You should have received a copy of the GNU Lesser General Public License |
| 15 | +// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE |
| 16 | +package ante |
| 17 | + |
| 18 | +import ( |
| 19 | + "fmt" |
| 20 | + |
| 21 | + errorsmod "cosmossdk.io/errors" |
| 22 | + sdk "github.com/cosmos/cosmos-sdk/types" |
| 23 | + errortypes "github.com/cosmos/cosmos-sdk/types/errors" |
| 24 | + "github.com/cosmos/cosmos-sdk/x/authz" |
| 25 | +) |
| 26 | + |
| 27 | +// maxNestedMsgs defines a cap for the number of nested messages on a MsgExec message |
| 28 | +const maxNestedMsgs = 6 |
| 29 | + |
| 30 | +// AuthzLimiterDecorator blocks certain msg types from being granted or executed |
| 31 | +// within the authorization module. |
| 32 | +type AuthzLimiterDecorator struct { |
| 33 | + // disabledMsgs is a set that contains type urls of unauthorized msgs. |
| 34 | + disabledMsgs map[string]struct{} |
| 35 | +} |
| 36 | + |
| 37 | +// NewAuthzLimiterDecorator creates a decorator to block certain msg types |
| 38 | +// from being granted or executed within authz. |
| 39 | +func NewAuthzLimiterDecorator(disabledMsgTypes []string) AuthzLimiterDecorator { |
| 40 | + disabledMsgs := make(map[string]struct{}) |
| 41 | + for _, url := range disabledMsgTypes { |
| 42 | + disabledMsgs[url] = struct{}{} |
| 43 | + } |
| 44 | + |
| 45 | + return AuthzLimiterDecorator{ |
| 46 | + disabledMsgs: disabledMsgs, |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +func (ald AuthzLimiterDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { |
| 51 | + if err := ald.checkDisabledMsgs(tx.GetMsgs(), false, 0); err != nil { |
| 52 | + return ctx, errorsmod.Wrapf(errortypes.ErrUnauthorized, err.Error()) |
| 53 | + } |
| 54 | + return next(ctx, tx, simulate) |
| 55 | +} |
| 56 | + |
| 57 | +// checkDisabledMsgs iterates through the msgs and returns an error if it finds any unauthorized msgs. |
| 58 | +// |
| 59 | +// This method is recursive as MsgExec's can wrap other MsgExecs. nestedMsgs sets a reasonable limit on |
| 60 | +// the total messages, regardless of how they are nested. |
| 61 | +func (ald AuthzLimiterDecorator) checkDisabledMsgs(msgs []sdk.Msg, isAuthzInnerMsg bool, nestedMsgs int) error { |
| 62 | + if nestedMsgs >= maxNestedMsgs { |
| 63 | + return fmt.Errorf("found more nested msgs than permitted. Limit is : %d", maxNestedMsgs) |
| 64 | + } |
| 65 | + for _, msg := range msgs { |
| 66 | + switch msg := msg.(type) { |
| 67 | + case *authz.MsgExec: |
| 68 | + innerMsgs, err := msg.GetMessages() |
| 69 | + if err != nil { |
| 70 | + return err |
| 71 | + } |
| 72 | + nestedMsgs++ |
| 73 | + if err := ald.checkDisabledMsgs(innerMsgs, true, nestedMsgs); err != nil { |
| 74 | + return err |
| 75 | + } |
| 76 | + case *authz.MsgGrant: |
| 77 | + authorization, err := msg.GetAuthorization() |
| 78 | + if err != nil { |
| 79 | + return err |
| 80 | + } |
| 81 | + |
| 82 | + url := authorization.MsgTypeURL() |
| 83 | + if ald.isDisabledMsg(url) { |
| 84 | + return fmt.Errorf("found disabled msg type: %s", url) |
| 85 | + } |
| 86 | + default: |
| 87 | + url := sdk.MsgTypeURL(msg) |
| 88 | + if isAuthzInnerMsg && ald.isDisabledMsg(url) { |
| 89 | + return fmt.Errorf("found disabled msg type: %s", url) |
| 90 | + } |
| 91 | + } |
| 92 | + } |
| 93 | + return nil |
| 94 | +} |
| 95 | + |
| 96 | +// isDisabledMsg returns true if the given message is in the set of restricted |
| 97 | +// messages from the AnteHandler. |
| 98 | +func (ald AuthzLimiterDecorator) isDisabledMsg(msgTypeURL string) bool { |
| 99 | + _, ok := ald.disabledMsgs[msgTypeURL] |
| 100 | + return ok |
| 101 | +} |
0 commit comments