Skip to content

Commit

Permalink
[FAB-3155] LSCC security checks at validation time
Browse files Browse the repository at this point in the history
Invocations of LSCC need to undergo some common validation (e.g. that they
are endorsed by as valid peer). Furthermore:
 - the instantiation policy should be satisfied
 - the read/write set should be coherent (e.g. it should only change the
   chaincode state whose instantiation policy is checked);

This change-set refactors the validator code and prepares fabric for the
two checks listed above, which will be delivered as a follow-up CS.

Change-Id: I6480261ce6ec2cc747e23b44159e08aa7a76c884
Signed-off-by: Alessandro Sorniotti <ale.linux@sopit.net>
  • Loading branch information
ale-linux committed Apr 19, 2017
1 parent eba4a20 commit 735878b
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 53 deletions.
28 changes: 28 additions & 0 deletions common/cauthdsl/cauthdsl_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
cb "github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/msp"

"sort"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/protos/utils"
)
Expand Down Expand Up @@ -111,6 +113,32 @@ func SignedByMspAdmin(mspId string) *cb.SignaturePolicyEnvelope {
return p
}

// SignedByAnyMember returns a policy that requires one valid
// signature from a member of any of the orgs whose ids are
// listed in the supplied string array
func SignedByAnyMember(ids []string) []byte {
// we create an array of principals, one principal
// per application MSP defined on this chain
sort.Strings(ids)
principals := make([]*msp.MSPPrincipal, len(ids))
sigspolicy := make([]*cb.SignaturePolicy, len(ids))
for i, id := range ids {
principals[i] = &msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_ROLE,
Principal: utils.MarshalOrPanic(&msp.MSPRole{Role: msp.MSPRole_MEMBER, MspIdentifier: id})}
sigspolicy[i] = SignedBy(int32(i))
}

// create the policy: it requires exactly 1 signature from any of the principals
p := &cb.SignaturePolicyEnvelope{
Version: 0,
Policy: NOutOf(1, sigspolicy),
Identities: principals,
}

return utils.MarshalOrPanic(p)
}

// And is a convenience method which utilizes NOutOf to produce And equivalent behavior
func And(lhs, rhs *cb.SignaturePolicy) *cb.SignaturePolicy {
return NOutOf(2, []*cb.SignaturePolicy{lhs, rhs})
Expand Down
52 changes: 33 additions & 19 deletions core/committer/txvalidator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ import (
ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
"github.com/hyperledger/fabric/msp"

"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
"github.com/op/go-logging"

"github.com/hyperledger/fabric/common/cauthdsl"
)

// Support provides all of the needed to evaluate the VSCC
Expand All @@ -45,6 +48,13 @@ type Support interface {

// Apply attempts to apply a configtx to become the new config
Apply(configtx *common.ConfigEnvelope) error

// PolicyManager returns the policies.Manager for the channel
PolicyManager() policies.Manager

// GetMSPIDs returns the IDs for the application MSPs
// that have been defined in the channel
GetMSPIDs(cid string) []string
}

//Validator interface which defines API to validate block transactions
Expand All @@ -58,7 +68,7 @@ type Validator interface {
// and vscc execution, in order to increase
// testability of txValidator
type vsccValidator interface {
VSCCValidateTx(payload *common.Payload, envBytes []byte) error
VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) error
}

// vsccValidator implementation which used to call
Expand Down Expand Up @@ -148,7 +158,7 @@ func (v *txValidator) Validate(block *common.Block) error {

//the payload is used to get headers
logger.Debug("Validating transaction vscc tx validate")
if err = v.vscc.VSCCValidateTx(payload, d); err != nil {
if err = v.vscc.VSCCValidateTx(payload, d, env); err != nil {
txID := txID
logger.Errorf("VSCCValidateTx for transaction txId = %s returned error %s", txID, err)
txsfltr.SetFlag(tIdx, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE)
Expand Down Expand Up @@ -191,7 +201,8 @@ func (v *txValidator) Validate(block *common.Block) error {
return nil
}

func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte) error {
func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) error {
// get channel header
chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil {
return err
Expand Down Expand Up @@ -226,22 +237,25 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b
return err
}

// LSCC should not undergo standard VSCC type of
// validation. It should instead go through system
// policy validation to determine whether the issuer
// is entitled to deploy a chaincode on our chain
// VSCCValidateTx should
if hdrExt.ChaincodeId.Name == "lscc" {
// TODO: until FAB-1934 is in, we need to stop here
logger.Debugf("Invocation of LSCC detected, no further VSCC validation necessary")
return nil
}

// obtain name of the VSCC and the policy from LSCC
vscc, policy, err := v.ccprovider.GetCCValidationInfoFromLSCC(ctxt, txid, nil, nil, chainID, hdrExt.ChaincodeId.Name)
if err != nil {
logger.Errorf("Unable to get chaincode data from LSCC for txid %s, due to %s", txid, err)
return err
var vscc string
var policy []byte
if hdrExt.ChaincodeId.Name != "lscc" {
// when we are validating any chaincode other than
// LSCC, we need to ask LSCC to give us the name
// of VSCC and of the policy that should be used

// obtain name of the VSCC and the policy from LSCC
vscc, policy, err = v.ccprovider.GetCCValidationInfoFromLSCC(ctxt, txid, nil, nil, chainID, hdrExt.ChaincodeId.Name)
if err != nil {
logger.Errorf("Unable to get chaincode data from LSCC for txid %s, due to %s", txid, err)
return err
}
} else {
// when we are validating LSCC, we use the default
// VSCC and a default policy that requires one signature
// from any of the members of the channel
vscc = "vscc"
policy = cauthdsl.SignedByAnyMember(v.support.GetMSPIDs(chainID))
}

// build arguments for VSCC invocation
Expand Down
10 changes: 10 additions & 0 deletions core/mocks/txvalidator/support.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package support

import (
mockpolicies "github.com/hyperledger/fabric/common/mocks/policies"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protos/common"
Expand All @@ -42,3 +44,11 @@ func (ms *Support) MSPManager() msp.MSPManager {
func (ms *Support) Apply(configtx *common.ConfigEnvelope) error {
return ms.ApplyVal
}

func (ms *Support) PolicyManager() policies.Manager {
return &mockpolicies.Manager{}
}

func (cs *Support) GetMSPIDs(cid string) []string {
return []string{"DEFAULT"}
}
2 changes: 1 addition & 1 deletion core/mocks/validator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ type MockVsccValidator struct {
}

// VSCCValidateTx does nothing
func (v *MockVsccValidator) VSCCValidateTx(payload *common.Payload, envBytes []byte) error {
func (v *MockVsccValidator) VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) error {
return nil
}
4 changes: 4 additions & 0 deletions core/peer/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ func (cs *chainSupport) Ledger() ledger.PeerLedger {
return cs.ledger
}

func (cs *chainSupport) GetMSPIDs(cid string) []string {
return GetMSPIDs(cid)
}

// chain is a local struct to manage objects in a chain
type chain struct {
cs *chainSupport
Expand Down
35 changes: 2 additions & 33 deletions core/scc/lscc/lscc.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,13 @@ package lscc
import (
"fmt"
"regexp"
"sort"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/cauthdsl"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/common/sysccprovider"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/protos/common"
mspprotos "github.com/hyperledger/fabric/protos/msp"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
"github.com/op/go-logging"
Expand Down Expand Up @@ -570,34 +567,6 @@ func (lscc *LifeCycleSysCC) Init(stub shim.ChaincodeStubInterface) pb.Response {
return shim.Success(nil)
}

// getDefaultEndorsementPolicy returns the default
// endorsement policy for the specified chain; it
// is used in case the deployer has not specified a
// custom one
func (lscc *LifeCycleSysCC) getDefaultEndorsementPolicy(chain string) []byte {
// we create an array of principals, one principal
// per application MSP defined on this chain
ids := peer.GetMSPIDs(chain)
sort.Strings(ids)
principals := make([]*mspprotos.MSPPrincipal, len(ids))
sigspolicy := make([]*common.SignaturePolicy, len(ids))
for i, id := range ids {
principals[i] = &mspprotos.MSPPrincipal{
PrincipalClassification: mspprotos.MSPPrincipal_ROLE,
Principal: utils.MarshalOrPanic(&mspprotos.MSPRole{Role: mspprotos.MSPRole_MEMBER, MspIdentifier: id})}
sigspolicy[i] = cauthdsl.SignedBy(int32(i))
}

// create the policy: it requires exactly 1 signature from any of the principals
p := &common.SignaturePolicyEnvelope{
Version: 0,
Policy: cauthdsl.NOutOf(1, sigspolicy),
Identities: principals,
}

return utils.MarshalOrPanic(p)
}

// Invoke implements lifecycle functions "deploy", "start", "stop", "upgrade".
// Deploy's arguments - {[]byte("deploy"), []byte(<chainname>), <unmarshalled pb.ChaincodeDeploymentSpec>}
//
Expand Down Expand Up @@ -647,7 +616,7 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response
if len(args) > 3 && len(args[3]) > 0 {
policy = args[3]
} else {
policy = lscc.getDefaultEndorsementPolicy(chainname)
policy = cauthdsl.SignedByAnyMember(peer.GetMSPIDs(chainname))
}

var escc []byte
Expand Down Expand Up @@ -693,7 +662,7 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response
if len(args) > 3 && len(args[3]) > 0 {
policy = args[3]
} else {
policy = lscc.getDefaultEndorsementPolicy(chainname)
policy = cauthdsl.SignedByAnyMember(peer.GetMSPIDs(chainname))
}

var escc []byte
Expand Down
59 changes: 59 additions & 0 deletions core/scc/vscc/validator_onevalidsignature.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ package vscc
import (
"fmt"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/cauthdsl"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/scc/lscc"
mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
"github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
Expand Down Expand Up @@ -142,9 +144,66 @@ func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface)
if err != nil {
return shim.Error(fmt.Sprintf("VSCC error: policy evaluation failed, err %s", err))
}

hdrExt, err := utils.GetChaincodeHeaderExtension(payl.Header)
if err != nil {
logger.Errorf("VSCC error: GetChaincodeHeaderExtension failed, err %s", err)
return shim.Error(err.Error())
}

// do some extra validation that is specific to lscc
if hdrExt.ChaincodeId.Name == "lscc" {
err = vscc.ValidateLSCCInvocation(cap)
if err != nil {
logger.Errorf("VSCC error: ValidateLSCCInvocation failed, err %s", err)
return shim.Error(err.Error())
}
}
}

logger.Debugf("VSCC exists successfully")

return shim.Success(nil)
}

func (vscc *ValidatorOneValidSignature) ValidateLSCCInvocation(cap *pb.ChaincodeActionPayload) error {
cpp, err := utils.GetChaincodeProposalPayload(cap.ChaincodeProposalPayload)
if err != nil {
logger.Errorf("VSCC error: GetChaincodeProposalPayload failed, err %s", err)
return err
}

cis := &pb.ChaincodeInvocationSpec{}
err = proto.Unmarshal(cpp.Input, cis)
if err != nil {
logger.Errorf("VSCC error: Unmarshal ChaincodeInvocationSpec failed, err %s", err)
return err
}

if cis == nil ||
cis.ChaincodeSpec == nil ||
cis.ChaincodeSpec.Input == nil ||
cis.ChaincodeSpec.Input.Args == nil {
logger.Errorf("VSCC error: committing invalid vscc invocation")
return fmt.Errorf("VSCC error: committing invalid vscc invocation")
}

lsccFunc := string(cis.ChaincodeSpec.Input.Args[0])
lsccArgs := cis.ChaincodeSpec.Input.Args[1:]

switch lsccFunc {
case lscc.DEPLOY:
case lscc.UPGRADE:
logger.Infof("VSCC info: validating invocation of lscc function %s on arguments %#v", lsccFunc, lsccArgs)

// TODO: two more crs are expected to fill this gap, as explained in FAB-3155
// 1) check that the invocation complies with the InstantiationPolicy
// 2) check that the read/write set is appropriate

return nil
default:
return fmt.Errorf("VSCC error: committing an invocation of function %s of lscc is invalid", lsccFunc)
}

return nil
}

0 comments on commit 735878b

Please sign in to comment.