Skip to content

Commit

Permalink
[FAB-2997] don't invoke some scc thru cc2cc
Browse files Browse the repository at this point in the history
This is the second of three fixes required for FAB-2997 (described in the
discussion for that jira item). In this change set we prevent
security-sensitive system chaincodes from being invoked through cc2cc
invocations.

Change-Id: Ie1be16243b2ea29d31cbaa0b19b5f08e9b0c12cc
Signed-off-by: Alessandro Sorniotti <ale.linux@sopit.net>
  • Loading branch information
ale-linux committed Apr 19, 2017
1 parent a3efa2c commit 9a4f02c
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 9 deletions.
54 changes: 54 additions & 0 deletions core/chaincode/exectransaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1475,6 +1475,60 @@ func TestChaincodeQueryChaincodeUsingInvoke(t *testing.T) {
theChaincodeSupport.Stop(ctxt, cccid2, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2})
}

// test that invoking a security-sensitive system chaincode fails
func TestChaincodeInvokesForbiddenSystemChaincode(t *testing.T) {
chainID := util.GetTestChainID()

lis, err := initPeer(chainID)
if err != nil {
t.Fail()
t.Logf("Error creating peer: %s", err)
}

defer finitPeer(lis, chainID)

var ctxt = context.Background()

var nextBlockNumber uint64

// Deploy second chaincode
url := "github.com/hyperledger/fabric/examples/chaincode/go/passthru"

cID := &pb.ChaincodeID{Name: "pthru", Path: url, Version: "0"}
f := "init"
args := util.ToChaincodeArgs(f)

spec := &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}

cccid := ccprovider.NewCCContext(chainID, "pthru", "0", "", false, nil, nil)

_, err = deploy(ctxt, cccid, spec, nextBlockNumber)
nextBlockNumber++
ccID := spec.ChaincodeId.Name
if err != nil {
t.Fail()
t.Logf("Error initializing chaincode %s(%s)", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

time.Sleep(time.Second)

// send an invoke to pass thru to invoke "escc" system chaincode
// this should fail
args = util.ToChaincodeArgs("escc/"+chainID, "getid", chainID, "pthru")

spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
// Invoke chaincode
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
if err == nil {
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
t.Logf("invoking <%s> should have failed", ccID)
t.Fail()
return
}
}

// Test the execution of a chaincode that invokes system chaincode
// uses the "pthru" chaincode to query "lscc" for the "pthru" chaincode
func TestChaincodeInvokesSystemChaincode(t *testing.T) {
Expand Down
28 changes: 20 additions & 8 deletions core/chaincode/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/common/sysccprovider"
ccintf "github.com/hyperledger/fabric/core/container/ccintf"
"github.com/hyperledger/fabric/core/container/ccintf"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/ledger/ledgerconfig"
"github.com/hyperledger/fabric/core/peer"
Expand Down Expand Up @@ -238,12 +238,20 @@ func (handler *Handler) deleteQueryIterator(txContext *transactionContext, txid
}

// Check if the transactor is allow to call this chaincode on this channel
func (handler *Handler) checkACL(signedProp *pb.SignedProposal, proposal *pb.Proposal, calledCC *ccParts) *pb.ChaincodeMessage {
// TODO: Decide what to pass in to verify that this transactor can access this
// channel (chID) and chaincode (ccID). Very likely we need the signedProposal
// which contains the sig and creator cert
func (handler *Handler) checkACL(signedProp *pb.SignedProposal, proposal *pb.Proposal, calledCC *ccParts) error {
// ensure that we don't invoke a system chaincode
// that is not invokable through a cc2cc invocation
if sysccprovider.GetSystemChaincodeProvider().IsSysCCAndNotInvokableCC2CC(calledCC.name) {
return fmt.Errorf("System chaincode %s cannot be invoked with a cc2cc invocation", calledCC.name)
}

// if we are here, all we know is that the invoked chaincode is either
// - a system chaincode that *is* invokable through a cc2cc
// (but we may still have to determine whether the invoker
// can perform this invocation)
// - an application chaincode (and we still need to determine
// whether the invoker can invoke it)

// If error, return ChaincodeMessage with type ChaincodeMessage_ERROR
return nil
}

Expand Down Expand Up @@ -1269,8 +1277,12 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) {
shorttxid(msg.Txid), calledCcParts.name, calledCcParts.suffix)
}

triggerNextStateMsg = handler.checkACL(txContext.signedProp, txContext.proposal, calledCcParts)
if triggerNextStateMsg != nil {
err := handler.checkACL(txContext.signedProp, txContext.proposal, calledCcParts)
if err != nil {
chaincodeLogger.Errorf("[%s] C-call-C %s on channel %s failed check ACL [%v]: [%s]",
shorttxid(msg.Txid), calledCcParts.name, calledCcParts.suffix, txContext.signedProp, err)
triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR,
Payload: []byte(err.Error()), Txid: msg.Txid}
return
}

Expand Down
4 changes: 4 additions & 0 deletions core/common/sysccprovider/sysccprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ package sysccprovider
type SystemChaincodeProvider interface {
// IsSysCC returns true if the supplied chaincode is a system chaincode
IsSysCC(name string) bool

// IsSysCCAndNotInvokableCC2CC returns true if the supplied chaincode
// is a system chaincode and is not invokable through a cc2cc invocation
IsSysCCAndNotInvokableCC2CC(name string) bool
}

var sccFactory SystemChaincodeProviderFactory
Expand Down
15 changes: 14 additions & 1 deletion core/scc/importsysccs.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ var systemChaincodes = []*SystemChaincode{
Path: "github.com/hyperledger/fabric/core/scc/lscc",
InitArgs: [][]byte{[]byte("")},
Chaincode: &lscc.LifeCycleSysCC{},
InvokableExternal: true, // lccc is invoked to deploy new chaincodes
InvokableExternal: true, // lscc is invoked to deploy new chaincodes
InvokableCC2CC: true, // lscc can be invoked by other chaincodes
},
{
Enabled: true,
Expand Down Expand Up @@ -116,6 +117,18 @@ func IsSysCCAndNotInvokable(name string) bool {
return false
}

// IsSysCCAndNotInvokableCC2CC returns true if the chaincode
// is a system chaincode and *CANNOT* be invoked through
// a cc2cc invocation
func IsSysCCAndNotInvokableCC2CC(name string) bool {
for _, sysCC := range systemChaincodes {
if sysCC.Name == name {
return !sysCC.InvokableCC2CC
}
}
return false
}

// MockRegisterSysCCs is used only for testing
// This is needed to break import cycle
func MockRegisterSysCCs(mockSysCCs []*SystemChaincode) []*SystemChaincode {
Expand Down
4 changes: 4 additions & 0 deletions core/scc/lscc/lscc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ func (c *mocksccProviderImpl) IsSysCC(name string) bool {
return true
}

func (c *mocksccProviderImpl) IsSysCCAndNotInvokableCC2CC(name string) bool {
return false
}

func register(stub *shim.MockStub, ccname string) error {
args := [][]byte{[]byte("register"), []byte(ccname)}
if res := stub.MockInvoke("1", args); res.Status != shim.OK {
Expand Down
6 changes: 6 additions & 0 deletions core/scc/sccproviderimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,9 @@ type sccProviderImpl struct {
func (c *sccProviderImpl) IsSysCC(name string) bool {
return IsSysCC(name)
}

// IsSysCCAndNotInvokableCC2CC returns true if the supplied chaincode is
// ia system chaincode and it NOT nvokable through a cc2cc invocation
func (c *sccProviderImpl) IsSysCCAndNotInvokableCC2CC(name string) bool {
return IsSysCCAndNotInvokableCC2CC(name)
}

0 comments on commit 9a4f02c

Please sign in to comment.