Skip to content

Commit

Permalink
Merge "[FAB-3155] LSCC security checks at validation time"
Browse files Browse the repository at this point in the history
  • Loading branch information
yacovm authored and Gerrit Code Review committed Apr 19, 2017
2 parents 7e0cff2 + 735878b commit 340ff90
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
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
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
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
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
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
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
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 340ff90

Please sign in to comment.