Skip to content

Commit

Permalink
[FAB-2969] Access control at LSCC
Browse files Browse the repository at this point in the history
This change-set does the following:
1. Add access control to lscc by verifying that the caller
has appropriate rights.
Tests have been modified to cover this access control
It is left to complete access control
for instantiation. This will be put in place
once the instantation process gets completed.

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

Change-Id: Ife15a69f963e33ce32704598e4febe24742bcb9c
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed Apr 23, 2017
1 parent 88ac3ba commit c3c1216
Show file tree
Hide file tree
Showing 6 changed files with 499 additions and 29 deletions.
26 changes: 22 additions & 4 deletions core/chaincode/exectransaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/ledger/util/couchdb"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/core/policy"
"github.com/hyperledger/fabric/core/scc"
pb "github.com/hyperledger/fabric/protos/peer"
putils "github.com/hyperledger/fabric/protos/utils"
Expand Down Expand Up @@ -93,6 +94,9 @@ func initPeer(chainIDs ...string) (net.Listener, error) {
ccStartupTimeout := time.Duration(chaincodeStartupTimeoutDefault) * time.Millisecond
pb.RegisterChaincodeSupportServer(grpcServer, NewChaincodeSupport(getPeerEndpoint, false, ccStartupTimeout))

// Mock policy checker
policy.RegisterPolicyCheckerFactory(&mockPolicyCheckerFactory{})

scc.RegisterSysCCs()

for _, id := range chainIDs {
Expand Down Expand Up @@ -313,15 +317,16 @@ func deploy2(ctx context.Context, cccid *ccprovider.CCContext, chaincodeDeployme
ccprovider.PutChaincodeIntoFS(chaincodeDeploymentSpec)

sysCCVers := util.GetSysCCVersion()
lsccid := ccprovider.NewCCContext(cccid.ChainID, cis.ChaincodeSpec.ChaincodeId.Name, sysCCVers, uuid, true, nil, nil)
sprop, prop := putils.MockSignedEndorserProposalOrPanic(cccid.ChainID, cis.ChaincodeSpec, []byte("Admin"), []byte("msg1"))
lsccid := ccprovider.NewCCContext(cccid.ChainID, cis.ChaincodeSpec.ChaincodeId.Name, sysCCVers, uuid, true, sprop, prop)

//write to lscc
if _, _, err = ExecuteWithErrorFilter(ctx, lsccid, cis); err != nil {
return nil, fmt.Errorf("Error deploying chaincode: %s", err)
return nil, fmt.Errorf("Error deploying chaincode (1): %s", err)
}

if b, _, err = ExecuteWithErrorFilter(ctx, cccid, chaincodeDeploymentSpec); err != nil {
return nil, fmt.Errorf("Error deploying chaincode: %s", err)
return nil, fmt.Errorf("Error deploying chaincode(2): %s", err)
}

return b, nil
Expand Down Expand Up @@ -356,7 +361,10 @@ func invokeWithVersion(ctx context.Context, chainID string, version string, spec
}
}()

sprop, prop := putils.MockSignedEndorserProposalOrPanic(util.GetTestChainID(), spec, creator, nil)
if len(creator) == 0 {
creator = []byte("Admin")
}
sprop, prop := putils.MockSignedEndorserProposalOrPanic(chainID, spec, creator, []byte("msg1"))
cccid := ccprovider.NewCCContext(chainID, cdInvocationSpec.ChaincodeSpec.ChaincodeId.Name, version, uuid, false, sprop, prop)
retval, ccevt, err = ExecuteWithErrorFilter(ctx, cccid, cdInvocationSpec)
if err != nil {
Expand Down Expand Up @@ -1797,3 +1805,13 @@ func (c *CreatorPolicy) Evaluate(signatureSet []*common.SignedData) error {
}
return fmt.Errorf("Creator not recognized [%s]", string(signatureSet[0].Identity))
}

type mockPolicyCheckerFactory struct{}

func (f *mockPolicyCheckerFactory) NewPolicyChecker() policy.PolicyChecker {
return policy.NewPolicyChecker(
peer.NewChannelPolicyManagerGetter(),
&policy.MockIdentityDeserializer{[]byte("Admin"), []byte("msg1")},
&policy.MockMSPPrincipalGetter{Principal: []byte("Admin")},
)
}
44 changes: 42 additions & 2 deletions core/committer/txvalidator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,11 +403,13 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b
// 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)
cd, err := v.getCDataForCC(hdrExt.ChaincodeId.Name)
if err != nil {
logger.Errorf("Unable to get chaincode data from LSCC for txid %s, due to %s", txid, err)
logger.Errorf("Unable to get chaincode data from ledger for txid %s, due to %s", txid, err)
return err
}
vscc = cd.Vscc
policy = cd.Policy
} else {
// when we are validating LSCC, we use the default
// VSCC and a default policy that requires one signature
Expand Down Expand Up @@ -442,3 +444,41 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b

return nil
}

func (v *vsccValidatorImpl) getCDataForCC(ccid string) (*ccprovider.ChaincodeData, error) {
l := v.support.Ledger()
if l == nil {
return nil, fmt.Errorf("nil ledger instance")
}

qe, err := l.NewQueryExecutor()
if err != nil {
return nil, fmt.Errorf("Could not retrieve QueryExecutor, error %s", err)
}
defer qe.Done()

bytes, err := qe.GetState("lscc", ccid)
if err != nil {
return nil, fmt.Errorf("Could not retrieve state for chaincode %s, error %s", ccid, err)
}

if bytes == nil {
return nil, fmt.Errorf("lscc's state for [%s] not found.", ccid)
}

cd := &ccprovider.ChaincodeData{}
err = proto.Unmarshal(bytes, cd)
if err != nil {
return nil, fmt.Errorf("Unmarshalling ChaincodeQueryResponse failed, error %s", err)
}

if cd.Vscc == "" {
return nil, fmt.Errorf("lscc's state for [%s] is invalid, vscc field must be set.", ccid)
}

if len(cd.Policy) == 0 {
return nil, fmt.Errorf("lscc's state for [%s] is invalid, policy field must be set.", ccid)
}

return cd, err
}
24 changes: 24 additions & 0 deletions core/policy/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,27 @@ func (p *policyChecker) CheckPolicyBySignedData(channelID, policyName string, sd

return nil
}

var pcFactory PolicyCheckerFactory

// PolicyCheckerFactory defines a factory interface so
// that the actual implementation can be injected
type PolicyCheckerFactory interface {
NewPolicyChecker() PolicyChecker
}

// RegisterPolicyCheckerFactory is to be called once to set
// the factory that will be used to obtain instances of PolicyChecker
func RegisterPolicyCheckerFactory(f PolicyCheckerFactory) {
pcFactory = f
}

// GetPolicyChecker returns instances of PolicyChecker;
// the actual implementation is controlled by the factory that
// is registered via RegisterPolicyCheckerFactory
func GetPolicyChecker() PolicyChecker {
if pcFactory == nil {
panic("The factory must be set first via RegisterPolicyCheckerFactory")
}
return pcFactory.NewPolicyChecker()
}
43 changes: 43 additions & 0 deletions core/policyprovider/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
Copyright IBM Corp. 2017 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package policyprovider

import (
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/core/policy"
"github.com/hyperledger/fabric/msp/mgmt"
)

// init is called when this package is loaded. This implementation registers the factory
func init() {
policy.RegisterPolicyCheckerFactory(&defaultFactory{})
}

type defaultFactory struct{}

func (f *defaultFactory) NewPolicyChecker() policy.PolicyChecker {
return policy.NewPolicyChecker(
peer.NewChannelPolicyManagerGetter(),
mgmt.GetLocalMSP(),
mgmt.NewLocalMSPPrincipalGetter(),
)
}

// GetPolicyChecker returns instances of PolicyChecker;
func GetPolicyChecker() policy.PolicyChecker {
return policy.GetPolicyChecker()
}
49 changes: 49 additions & 0 deletions core/scc/lscc/lscc.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ import (
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/cauthdsl"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/common/policies"
"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/core/policy"
"github.com/hyperledger/fabric/core/policyprovider"
"github.com/hyperledger/fabric/msp/mgmt"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
)
Expand Down Expand Up @@ -82,6 +86,10 @@ type LifeCycleSysCC struct {
// methods of the system chaincode package without
// import cycles
sccprovider sysccprovider.SystemChaincodeProvider

// policyChecker is the interface used to perform
// access control
policyChecker policy.PolicyChecker
}

//----------------errors---------------
Expand Down Expand Up @@ -564,6 +572,10 @@ func (lscc *LifeCycleSysCC) executeUpgrade(stub shim.ChaincodeStubInterface, cha
//Init only initializes the system chaincode provider
func (lscc *LifeCycleSysCC) Init(stub shim.ChaincodeStubInterface) pb.Response {
lscc.sccprovider = sysccprovider.GetSystemChaincodeProvider()

// Init policy checker for access control
lscc.policyChecker = policyprovider.GetPolicyChecker()

return shim.Success(nil)
}

Expand All @@ -580,12 +592,24 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response

function := string(args[0])

// Handle ACL:
// 1. get the signed proposal
sp, err := stub.GetSignedProposal()
if err != nil {
return shim.Error(fmt.Sprintf("Failed retrieving signed proposal on executing %s with error %s", function, err))
}

switch function {
case INSTALL:
if len(args) < 2 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}

// 2. check local MSP Admins policy
if err = lscc.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp); err != nil {
return shim.Error(fmt.Sprintf("Authorization for INSTALL on %s has been denied with error %s", args[1], err))
}

depSpec := args[1]

err := lscc.executeInstall(stub, depSpec)
Expand All @@ -598,6 +622,9 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}

// TODO: add access control check
// once the instantiation process will be completed.

//chain the chaincode shoud be associated with. It
//should be created with a register call
chainname := string(args[1])
Expand Down Expand Up @@ -652,6 +679,9 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response
return shim.Error(InvalidChainNameErr(chainname).Error())
}

// TODO: add access control check
// once the instantiation process will be completed.

depSpec := args[2]

// optional arguments here (they can each be nil and may or may not be present)
Expand Down Expand Up @@ -696,6 +726,13 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response
chain := string(args[1])
ccname := string(args[2])

// 2. check local Channel Readers policy
// Notice that this information are already available on the ledger
// therefore we enforce here that the caller is reader of the channel.
if err = lscc.policyChecker.CheckPolicy(chain, policies.ChannelApplicationReaders, sp); err != nil {
return shim.Error(fmt.Sprintf("Authorization for %s on channel %s has been denied with error %s", function, args[1], err))
}

cdbytes, err := lscc.getCCInstance(stub, ccname)
if err != nil {
logger.Errorf("error getting chaincode %s on channel: %s(err:%s)", ccname, chain, err)
Expand All @@ -722,11 +759,23 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response
if len(args) != 1 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}

// 2. check local MSP Admins policy
if err = lscc.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp); err != nil {
return shim.Error(fmt.Sprintf("Authorization for GETCHAINCODES on channel %s has been denied with error %s", args[0], err))
}

return lscc.getChaincodes(stub)
case GETINSTALLEDCHAINCODES:
if len(args) != 1 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}

// 2. check local MSP Admins policy
if err = lscc.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp); err != nil {
return shim.Error(fmt.Sprintf("Authorization for GETINSTALLEDCHAINCODES on channel %s has been denied with error %s", args[0], err))
}

return lscc.getInstalledChaincodes()
}

Expand Down
Loading

0 comments on commit c3c1216

Please sign in to comment.