Skip to content

Commit

Permalink
[FAB-2103] Adding ACL enforcement for CC2CC
Browse files Browse the repository at this point in the history
This change-set does the following:
1. It adds ACL checks to CC2CC invocation.
The checks are the following:
- If a system chaincode is invoked then the call is just authorized.
This assumes that the system chaincodes perform their own
access control.
- If a normal chaincode is invoked then it is checked that the
proposal's creator is a writer on the destination channel.

This change-set comes in the context of:
https://jira.hyperledger.org/browse/FAB-2103

Change-Id: I7d97fbcb7bc3016bdc235b28efab6c60f3da583c
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed Apr 22, 2017
1 parent 0fd4f6f commit edd0c07
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 92 deletions.
3 changes: 2 additions & 1 deletion common/configtx/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/hyperledger/fabric/common/configtx/api"
mockconfigtx "github.com/hyperledger/fabric/common/mocks/configtx"
mockpolicies "github.com/hyperledger/fabric/common/mocks/policies"
"github.com/hyperledger/fabric/common/policies"
cb "github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"

Expand Down Expand Up @@ -354,7 +355,7 @@ func TestUnchangedConfigViolatesPolicy(t *testing.T) {
}

// Set the mock policy to error
initializer.Resources.PolicyManagerVal.PolicyMap = make(map[string]*mockpolicies.Policy)
initializer.Resources.PolicyManagerVal.PolicyMap = make(map[string]policies.Policy)
initializer.Resources.PolicyManagerVal.PolicyMap["foo"] = &mockpolicies.Policy{Err: fmt.Errorf("err")}

newConfig := makeConfigUpdateEnvelope(
Expand Down
2 changes: 1 addition & 1 deletion common/mocks/policies/policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type Manager struct {
BasePathVal string

// PolicyMap is returned is used to look up Policies in
PolicyMap map[string]*Policy
PolicyMap map[string]policies.Policy

// SubManagers is used for the return value of Manager
SubManagersMap map[string]*Manager
Expand Down
2 changes: 1 addition & 1 deletion core/chaincode/concurrency_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func TestExecuteConcurrentInvokes(t *testing.T) {
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: chaincodeID, Input: &pb.ChaincodeInput{Args: args}}

//start with a new background
_, _, results[qnum], err = invoke(context.Background(), chainID, spec, nextBlockNumber)
_, _, results[qnum], err = invoke(context.Background(), chainID, spec, nextBlockNumber, nil)

if err != nil {
errs[qnum] = fmt.Errorf("Error executing <%s>: %s", chaincodeID.Name, err)
Expand Down
195 changes: 145 additions & 50 deletions core/chaincode/exectransaction_test.go

Large diffs are not rendered by default.

27 changes: 26 additions & 1 deletion core/chaincode/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ import (

"github.com/golang/protobuf/proto"
commonledger "github.com/hyperledger/fabric/common/ledger"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/common/sysccprovider"
"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"
"github.com/hyperledger/fabric/core/policy"
"github.com/hyperledger/fabric/msp/mgmt"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/looplab/fsm"
logging "github.com/op/go-logging"
Expand Down Expand Up @@ -85,6 +88,10 @@ type ccParts struct {
suffix string //for now just the chain name
}

func (p *ccParts) String() string {
return p.suffix + "." + p.name + "#" + p.version
}

// Handler responsbile for management of Peer's side of chaincode stream
type Handler struct {
sync.RWMutex
Expand All @@ -106,6 +113,8 @@ type Handler struct {

// used to do Send after making sure the state transition is complete
nextState chan *nextStateInfo

policyChecker policy.PolicyChecker
}

func shorttxid(txid string) string {
Expand Down Expand Up @@ -251,7 +260,17 @@ func (handler *Handler) checkACL(signedProp *pb.SignedProposal, proposal *pb.Pro
// - an application chaincode (and we still need to determine
// whether the invoker can invoke it)

return nil
if sysccprovider.GetSystemChaincodeProvider().IsSysCC(calledCC.name) {
// Allow this call
return nil
}

// A Nil signedProp will be rejected for non-system chaincodes
if signedProp == nil {
return fmt.Errorf("Signed Proposal must not be nil from caller [%s]", calledCC.String())
}

return handler.policyChecker.CheckPolicy(calledCC.suffix, policies.ChannelApplicationWriters, signedProp)
}

//THIS CAN BE REMOVED ONCE WE FULL SUPPORT (Invoke) CONFIDENTIALITY WITH CC-CALLING-CC
Expand Down Expand Up @@ -456,6 +475,12 @@ func newChaincodeSupportHandler(chaincodeSupport *ChaincodeSupport, peerChatStre
},
)

v.policyChecker = policy.NewPolicyChecker(
peer.NewChannelPolicyManagerGetter(),
mgmt.GetLocalMSP(),
mgmt.NewLocalMSPPrincipalGetter(),
)

return v
}

Expand Down
4 changes: 2 additions & 2 deletions core/chaincode/systemchaincode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func deploySampleSysCC(t *testing.T, ctxt context.Context, chainID string) error

spec := &pb.ChaincodeSpec{Type: 1, ChaincodeId: &pb.ChaincodeID{Name: "sample_syscc", Path: url, Version: sysCCVers}, Input: &pb.ChaincodeInput{Args: args}}
var nextBlockNumber uint64
_, _, _, err := invokeWithVersion(ctxt, chainID, sysCCVers, spec, nextBlockNumber)
_, _, _, err := invokeWithVersion(ctxt, chainID, sysCCVers, spec, nextBlockNumber, nil)
nextBlockNumber++

cccid := ccprovider.NewCCContext(chainID, "sample_syscc", sysCCVers, "", true, nil, nil)
Expand All @@ -119,7 +119,7 @@ func deploySampleSysCC(t *testing.T, ctxt context.Context, chainID string) error
f = "getval"
args = util.ToChaincodeArgs(f, "greeting")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: &pb.ChaincodeID{Name: "sample_syscc", Path: url, Version: sysCCVers}, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invokeWithVersion(ctxt, chainID, sysCCVers, spec, nextBlockNumber)
_, _, _, err = invokeWithVersion(ctxt, chainID, sysCCVers, spec, nextBlockNumber, nil)
if err != nil {
theChaincodeSupport.Stop(ctxt, cccid, cdsforStop)
t.Logf("Error invoking sample_syscc: %s", err)
Expand Down
4 changes: 2 additions & 2 deletions core/chaincode/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func TestUpgradeCC(t *testing.T) {
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: chaincodeID, Input: &pb.ChaincodeInput{Args: qArgs}}

//Do not increment block number here because, the block will not be committted because of error
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber, nil)
if err == nil {
t.Fail()
t.Logf("querying chaincode exampl01 should fail transaction: %s", err)
Expand Down Expand Up @@ -200,7 +200,7 @@ func TestUpgradeCC(t *testing.T) {
//go back and do the same query now
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: chaincodeID, Input: &pb.ChaincodeInput{Args: qArgs}}
nextBlockNumber++
_, _, _, err = invokeWithVersion(ctxt, chainID, cccid2.Version, spec, nextBlockNumber)
_, _, _, err = invokeWithVersion(ctxt, chainID, cccid2.Version, spec, nextBlockNumber, nil)

if err != nil {
t.Fail()
Expand Down
36 changes: 11 additions & 25 deletions core/endorser/endorser.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ import (
"github.com/hyperledger/fabric/core/common/validation"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/core/policy"
syscc "github.com/hyperledger/fabric/core/scc"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/msp/mgmt"
"github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
putils "github.com/hyperledger/fabric/protos/utils"
Expand All @@ -47,41 +49,25 @@ var endorserLogger = logging.MustGetLogger("endorser")

// Endorser provides the Endorser service ProcessProposal
type Endorser struct {
policyChecker policy.PolicyChecker
}

// NewEndorserServer creates and returns a new Endorser server instance.
func NewEndorserServer() pb.EndorserServer {
e := new(Endorser)
e.policyChecker = policy.NewPolicyChecker(
peer.NewChannelPolicyManagerGetter(),
mgmt.GetLocalMSP(),
mgmt.NewLocalMSPPrincipalGetter(),
)

return e
}

// checkACL checks that the supplied proposal complies
// with the writers policy of the chain
func (*Endorser) checkACL(signedProp *pb.SignedProposal, chdr *common.ChannelHeader, shdr *common.SignatureHeader, hdrext *pb.ChaincodeHeaderExtension) error {
// get policy manager to check ACLs
pm := peer.GetPolicyManager(chdr.ChannelId)
if pm == nil {
return fmt.Errorf("No policy manager available for chain %s", chdr.ChannelId)
}

// access the policy to use to validate this proposal
policy, _ := pm.GetPolicy(policies.ChannelApplicationWriters)

// evaluate that this proposal complies with the writers
err := policy.Evaluate(
[]*common.SignedData{{
Data: signedProp.ProposalBytes,
Identity: shdr.Creator,
Signature: signedProp.Signature,
}})
if err != nil {
return fmt.Errorf("The proposal does not comply with the %s for channel %s, error %s",
policies.ChannelApplicationWriters,
chdr.ChannelId,
err)
}

return nil
func (e *Endorser) checkACL(signedProp *pb.SignedProposal, chdr *common.ChannelHeader, shdr *common.SignatureHeader, hdrext *pb.ChaincodeHeaderExtension) error {
return e.policyChecker.CheckPolicy(chdr.ChannelId, policies.ChannelApplicationWriters, signedProp)
}

//TODO - check for escc and vscc
Expand Down
4 changes: 2 additions & 2 deletions core/endorser/endorser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ func TestWritersACLFail(t *testing.T) {
Err: errors.New("The creator of this proposal does not fulfil the writers policy of this chain"),
}
pm := peer.GetPolicyManager(chainID)
pm.(*mockpolicies.Manager).PolicyMap = map[string]*mockpolicies.Policy{policies.ChannelApplicationWriters: rejectpolicy}
pm.(*mockpolicies.Manager).PolicyMap = map[string]policies.Policy{policies.ChannelApplicationWriters: rejectpolicy}

f = "invoke"
invokeArgs := append([]string{f}, args...)
Expand Down Expand Up @@ -594,7 +594,7 @@ func TestAdminACLFail(t *testing.T) {
Err: errors.New("The creator of this proposal does not fulfil the writers policy of this chain"),
}
pm := peer.GetPolicyManager(chainID)
pm.(*mockpolicies.Manager).PolicyMap = map[string]*mockpolicies.Policy{policies.ChannelApplicationAdmins: rejectpolicy}
pm.(*mockpolicies.Manager).PolicyMap = map[string]policies.Policy{policies.ChannelApplicationAdmins: rejectpolicy}

var ctxt = context.Background()

Expand Down
7 changes: 3 additions & 4 deletions core/peer/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,6 @@ func MockCreateChain(cid string) error {
return err
}

chains.Lock()
defer chains.Unlock()

// Here we need to mock also the policy manager
// in order for the ACL to be checked
initializer := mockconfigtx.Initializer{
Expand All @@ -282,11 +279,13 @@ func MockCreateChain(cid string) error {
Transactional: mockconfigtx.Transactional{},
},
}

manager := &mockconfigtx.Manager{
Initializer: initializer,
}

chains.Lock()
defer chains.Unlock()

chains.list[cid] = &chain{
cs: &chainSupport{
Manager: manager,
Expand Down
10 changes: 7 additions & 3 deletions examples/chaincode/go/chaincode_example04/chaincode_example04.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string
var eventVal int // State of event
var err error

if len(args) != 3 {
return shim.Error("Incorrect number of arguments. Expecting 3")
if len(args) != 3 && len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 3 or 4")
}

chainCodeToCall := args[0]
Expand All @@ -73,6 +73,10 @@ func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string
if err != nil {
return shim.Error("Expected integer value for event state change")
}
channelID := ""
if len(args) == 4 {
channelID = args[3]
}

if eventVal != 1 {
fmt.Printf("Unexpected event. Doing nothing\n")
Expand All @@ -81,7 +85,7 @@ func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string

f := "invoke"
invokeArgs := util.ToChaincodeArgs(f, "a", "b", "10")
response := stub.InvokeChaincode(chainCodeToCall, invokeArgs, "")
response := stub.InvokeChaincode(chainCodeToCall, invokeArgs, channelID)
if response.Status != shim.OK {
errStr := fmt.Sprintf("Failed to invoke chaincode. Got error: %s", string(response.Payload))
fmt.Printf(errStr)
Expand Down

0 comments on commit edd0c07

Please sign in to comment.