From 735878b6755fe5f18b10c5fc2ca39584d45537bc Mon Sep 17 00:00:00 2001 From: Alessandro Sorniotti Date: Thu, 23 Feb 2017 12:06:42 +0100 Subject: [PATCH] [FAB-3155] LSCC security checks at validation time 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 --- common/cauthdsl/cauthdsl_builder.go | 28 ++++++++++ core/committer/txvalidator/validator.go | 52 ++++++++++------- core/mocks/txvalidator/support.go | 10 ++++ core/mocks/validator/validator.go | 2 +- core/peer/peer.go | 4 ++ core/scc/lscc/lscc.go | 35 +----------- core/scc/vscc/validator_onevalidsignature.go | 59 ++++++++++++++++++++ 7 files changed, 137 insertions(+), 53 deletions(-) diff --git a/common/cauthdsl/cauthdsl_builder.go b/common/cauthdsl/cauthdsl_builder.go index ee5489360d8..20d36484b7f 100644 --- a/common/cauthdsl/cauthdsl_builder.go +++ b/common/cauthdsl/cauthdsl_builder.go @@ -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" ) @@ -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}) diff --git a/core/committer/txvalidator/validator.go b/core/committer/txvalidator/validator.go index b01ccbb733d..755ac7eab81 100644 --- a/core/committer/txvalidator/validator.go +++ b/core/committer/txvalidator/validator.go @@ -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 @@ -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 @@ -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 @@ -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) @@ -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 @@ -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 diff --git a/core/mocks/txvalidator/support.go b/core/mocks/txvalidator/support.go index 770c9de926a..a40e9b0a63d 100644 --- a/core/mocks/txvalidator/support.go +++ b/core/mocks/txvalidator/support.go @@ -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" @@ -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"} +} diff --git a/core/mocks/validator/validator.go b/core/mocks/validator/validator.go index 8183788282f..41e133bb690 100644 --- a/core/mocks/validator/validator.go +++ b/core/mocks/validator/validator.go @@ -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 } diff --git a/core/peer/peer.go b/core/peer/peer.go index c9e77513540..d766ea83afd 100644 --- a/core/peer/peer.go +++ b/core/peer/peer.go @@ -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 diff --git a/core/scc/lscc/lscc.go b/core/scc/lscc/lscc.go index 5e9f2c6d95b..0ca4382063a 100644 --- a/core/scc/lscc/lscc.go +++ b/core/scc/lscc/lscc.go @@ -19,7 +19,6 @@ package lscc import ( "fmt" "regexp" - "sort" "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/common/cauthdsl" @@ -27,8 +26,6 @@ import ( "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" @@ -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(), } // @@ -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 @@ -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 diff --git a/core/scc/vscc/validator_onevalidsignature.go b/core/scc/vscc/validator_onevalidsignature.go index bc49edc7e0a..74668af18bf 100644 --- a/core/scc/vscc/validator_onevalidsignature.go +++ b/core/scc/vscc/validator_onevalidsignature.go @@ -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" @@ -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 +}