Skip to content

Commit 735878b

Browse files
committed
[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 <[email protected]>
1 parent eba4a20 commit 735878b

File tree

7 files changed

+137
-53
lines changed

7 files changed

+137
-53
lines changed

common/cauthdsl/cauthdsl_builder.go

+28
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
cb "github.com/hyperledger/fabric/protos/common"
2121
"github.com/hyperledger/fabric/protos/msp"
2222

23+
"sort"
24+
2325
"github.com/golang/protobuf/proto"
2426
"github.com/hyperledger/fabric/protos/utils"
2527
)
@@ -111,6 +113,32 @@ func SignedByMspAdmin(mspId string) *cb.SignaturePolicyEnvelope {
111113
return p
112114
}
113115

116+
// SignedByAnyMember returns a policy that requires one valid
117+
// signature from a member of any of the orgs whose ids are
118+
// listed in the supplied string array
119+
func SignedByAnyMember(ids []string) []byte {
120+
// we create an array of principals, one principal
121+
// per application MSP defined on this chain
122+
sort.Strings(ids)
123+
principals := make([]*msp.MSPPrincipal, len(ids))
124+
sigspolicy := make([]*cb.SignaturePolicy, len(ids))
125+
for i, id := range ids {
126+
principals[i] = &msp.MSPPrincipal{
127+
PrincipalClassification: msp.MSPPrincipal_ROLE,
128+
Principal: utils.MarshalOrPanic(&msp.MSPRole{Role: msp.MSPRole_MEMBER, MspIdentifier: id})}
129+
sigspolicy[i] = SignedBy(int32(i))
130+
}
131+
132+
// create the policy: it requires exactly 1 signature from any of the principals
133+
p := &cb.SignaturePolicyEnvelope{
134+
Version: 0,
135+
Policy: NOutOf(1, sigspolicy),
136+
Identities: principals,
137+
}
138+
139+
return utils.MarshalOrPanic(p)
140+
}
141+
114142
// And is a convenience method which utilizes NOutOf to produce And equivalent behavior
115143
func And(lhs, rhs *cb.SignaturePolicy) *cb.SignaturePolicy {
116144
return NOutOf(2, []*cb.SignaturePolicy{lhs, rhs})

core/committer/txvalidator/validator.go

+33-19
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ import (
2929
ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
3030
"github.com/hyperledger/fabric/msp"
3131

32+
"github.com/hyperledger/fabric/common/policies"
3233
"github.com/hyperledger/fabric/protos/common"
3334
"github.com/hyperledger/fabric/protos/peer"
3435
"github.com/hyperledger/fabric/protos/utils"
3536
"github.com/op/go-logging"
37+
38+
"github.com/hyperledger/fabric/common/cauthdsl"
3639
)
3740

3841
// Support provides all of the needed to evaluate the VSCC
@@ -45,6 +48,13 @@ type Support interface {
4548

4649
// Apply attempts to apply a configtx to become the new config
4750
Apply(configtx *common.ConfigEnvelope) error
51+
52+
// PolicyManager returns the policies.Manager for the channel
53+
PolicyManager() policies.Manager
54+
55+
// GetMSPIDs returns the IDs for the application MSPs
56+
// that have been defined in the channel
57+
GetMSPIDs(cid string) []string
4858
}
4959

5060
//Validator interface which defines API to validate block transactions
@@ -58,7 +68,7 @@ type Validator interface {
5868
// and vscc execution, in order to increase
5969
// testability of txValidator
6070
type vsccValidator interface {
61-
VSCCValidateTx(payload *common.Payload, envBytes []byte) error
71+
VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) error
6272
}
6373

6474
// vsccValidator implementation which used to call
@@ -148,7 +158,7 @@ func (v *txValidator) Validate(block *common.Block) error {
148158

149159
//the payload is used to get headers
150160
logger.Debug("Validating transaction vscc tx validate")
151-
if err = v.vscc.VSCCValidateTx(payload, d); err != nil {
161+
if err = v.vscc.VSCCValidateTx(payload, d, env); err != nil {
152162
txID := txID
153163
logger.Errorf("VSCCValidateTx for transaction txId = %s returned error %s", txID, err)
154164
txsfltr.SetFlag(tIdx, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE)
@@ -191,7 +201,8 @@ func (v *txValidator) Validate(block *common.Block) error {
191201
return nil
192202
}
193203

194-
func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte) error {
204+
func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) error {
205+
// get channel header
195206
chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
196207
if err != nil {
197208
return err
@@ -226,22 +237,25 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b
226237
return err
227238
}
228239

229-
// LSCC should not undergo standard VSCC type of
230-
// validation. It should instead go through system
231-
// policy validation to determine whether the issuer
232-
// is entitled to deploy a chaincode on our chain
233-
// VSCCValidateTx should
234-
if hdrExt.ChaincodeId.Name == "lscc" {
235-
// TODO: until FAB-1934 is in, we need to stop here
236-
logger.Debugf("Invocation of LSCC detected, no further VSCC validation necessary")
237-
return nil
238-
}
239-
240-
// obtain name of the VSCC and the policy from LSCC
241-
vscc, policy, err := v.ccprovider.GetCCValidationInfoFromLSCC(ctxt, txid, nil, nil, chainID, hdrExt.ChaincodeId.Name)
242-
if err != nil {
243-
logger.Errorf("Unable to get chaincode data from LSCC for txid %s, due to %s", txid, err)
244-
return err
240+
var vscc string
241+
var policy []byte
242+
if hdrExt.ChaincodeId.Name != "lscc" {
243+
// when we are validating any chaincode other than
244+
// LSCC, we need to ask LSCC to give us the name
245+
// of VSCC and of the policy that should be used
246+
247+
// obtain name of the VSCC and the policy from LSCC
248+
vscc, policy, err = v.ccprovider.GetCCValidationInfoFromLSCC(ctxt, txid, nil, nil, chainID, hdrExt.ChaincodeId.Name)
249+
if err != nil {
250+
logger.Errorf("Unable to get chaincode data from LSCC for txid %s, due to %s", txid, err)
251+
return err
252+
}
253+
} else {
254+
// when we are validating LSCC, we use the default
255+
// VSCC and a default policy that requires one signature
256+
// from any of the members of the channel
257+
vscc = "vscc"
258+
policy = cauthdsl.SignedByAnyMember(v.support.GetMSPIDs(chainID))
245259
}
246260

247261
// build arguments for VSCC invocation

core/mocks/txvalidator/support.go

+10
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
package support
1818

1919
import (
20+
mockpolicies "github.com/hyperledger/fabric/common/mocks/policies"
21+
"github.com/hyperledger/fabric/common/policies"
2022
"github.com/hyperledger/fabric/core/ledger"
2123
"github.com/hyperledger/fabric/msp"
2224
"github.com/hyperledger/fabric/protos/common"
@@ -42,3 +44,11 @@ func (ms *Support) MSPManager() msp.MSPManager {
4244
func (ms *Support) Apply(configtx *common.ConfigEnvelope) error {
4345
return ms.ApplyVal
4446
}
47+
48+
func (ms *Support) PolicyManager() policies.Manager {
49+
return &mockpolicies.Manager{}
50+
}
51+
52+
func (cs *Support) GetMSPIDs(cid string) []string {
53+
return []string{"DEFAULT"}
54+
}

core/mocks/validator/validator.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ type MockVsccValidator struct {
3232
}
3333

3434
// VSCCValidateTx does nothing
35-
func (v *MockVsccValidator) VSCCValidateTx(payload *common.Payload, envBytes []byte) error {
35+
func (v *MockVsccValidator) VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) error {
3636
return nil
3737
}

core/peer/peer.go

+4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ func (cs *chainSupport) Ledger() ledger.PeerLedger {
6464
return cs.ledger
6565
}
6666

67+
func (cs *chainSupport) GetMSPIDs(cid string) []string {
68+
return GetMSPIDs(cid)
69+
}
70+
6771
// chain is a local struct to manage objects in a chain
6872
type chain struct {
6973
cs *chainSupport

core/scc/lscc/lscc.go

+2-33
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,13 @@ package lscc
1919
import (
2020
"fmt"
2121
"regexp"
22-
"sort"
2322

2423
"github.com/golang/protobuf/proto"
2524
"github.com/hyperledger/fabric/common/cauthdsl"
2625
"github.com/hyperledger/fabric/core/chaincode/shim"
2726
"github.com/hyperledger/fabric/core/common/ccprovider"
2827
"github.com/hyperledger/fabric/core/common/sysccprovider"
2928
"github.com/hyperledger/fabric/core/peer"
30-
"github.com/hyperledger/fabric/protos/common"
31-
mspprotos "github.com/hyperledger/fabric/protos/msp"
3229
pb "github.com/hyperledger/fabric/protos/peer"
3330
"github.com/hyperledger/fabric/protos/utils"
3431
"github.com/op/go-logging"
@@ -570,34 +567,6 @@ func (lscc *LifeCycleSysCC) Init(stub shim.ChaincodeStubInterface) pb.Response {
570567
return shim.Success(nil)
571568
}
572569

573-
// getDefaultEndorsementPolicy returns the default
574-
// endorsement policy for the specified chain; it
575-
// is used in case the deployer has not specified a
576-
// custom one
577-
func (lscc *LifeCycleSysCC) getDefaultEndorsementPolicy(chain string) []byte {
578-
// we create an array of principals, one principal
579-
// per application MSP defined on this chain
580-
ids := peer.GetMSPIDs(chain)
581-
sort.Strings(ids)
582-
principals := make([]*mspprotos.MSPPrincipal, len(ids))
583-
sigspolicy := make([]*common.SignaturePolicy, len(ids))
584-
for i, id := range ids {
585-
principals[i] = &mspprotos.MSPPrincipal{
586-
PrincipalClassification: mspprotos.MSPPrincipal_ROLE,
587-
Principal: utils.MarshalOrPanic(&mspprotos.MSPRole{Role: mspprotos.MSPRole_MEMBER, MspIdentifier: id})}
588-
sigspolicy[i] = cauthdsl.SignedBy(int32(i))
589-
}
590-
591-
// create the policy: it requires exactly 1 signature from any of the principals
592-
p := &common.SignaturePolicyEnvelope{
593-
Version: 0,
594-
Policy: cauthdsl.NOutOf(1, sigspolicy),
595-
Identities: principals,
596-
}
597-
598-
return utils.MarshalOrPanic(p)
599-
}
600-
601570
// Invoke implements lifecycle functions "deploy", "start", "stop", "upgrade".
602571
// Deploy's arguments - {[]byte("deploy"), []byte(<chainname>), <unmarshalled pb.ChaincodeDeploymentSpec>}
603572
//
@@ -647,7 +616,7 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response
647616
if len(args) > 3 && len(args[3]) > 0 {
648617
policy = args[3]
649618
} else {
650-
policy = lscc.getDefaultEndorsementPolicy(chainname)
619+
policy = cauthdsl.SignedByAnyMember(peer.GetMSPIDs(chainname))
651620
}
652621

653622
var escc []byte
@@ -693,7 +662,7 @@ func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response
693662
if len(args) > 3 && len(args[3]) > 0 {
694663
policy = args[3]
695664
} else {
696-
policy = lscc.getDefaultEndorsementPolicy(chainname)
665+
policy = cauthdsl.SignedByAnyMember(peer.GetMSPIDs(chainname))
697666
}
698667

699668
var escc []byte

core/scc/vscc/validator_onevalidsignature.go

+59
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ package vscc
1919
import (
2020
"fmt"
2121

22+
"github.com/golang/protobuf/proto"
2223
"github.com/hyperledger/fabric/common/cauthdsl"
2324
"github.com/hyperledger/fabric/core/chaincode/shim"
25+
"github.com/hyperledger/fabric/core/scc/lscc"
2426
mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
2527
"github.com/hyperledger/fabric/protos/common"
2628
pb "github.com/hyperledger/fabric/protos/peer"
@@ -142,9 +144,66 @@ func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface)
142144
if err != nil {
143145
return shim.Error(fmt.Sprintf("VSCC error: policy evaluation failed, err %s", err))
144146
}
147+
148+
hdrExt, err := utils.GetChaincodeHeaderExtension(payl.Header)
149+
if err != nil {
150+
logger.Errorf("VSCC error: GetChaincodeHeaderExtension failed, err %s", err)
151+
return shim.Error(err.Error())
152+
}
153+
154+
// do some extra validation that is specific to lscc
155+
if hdrExt.ChaincodeId.Name == "lscc" {
156+
err = vscc.ValidateLSCCInvocation(cap)
157+
if err != nil {
158+
logger.Errorf("VSCC error: ValidateLSCCInvocation failed, err %s", err)
159+
return shim.Error(err.Error())
160+
}
161+
}
145162
}
146163

147164
logger.Debugf("VSCC exists successfully")
148165

149166
return shim.Success(nil)
150167
}
168+
169+
func (vscc *ValidatorOneValidSignature) ValidateLSCCInvocation(cap *pb.ChaincodeActionPayload) error {
170+
cpp, err := utils.GetChaincodeProposalPayload(cap.ChaincodeProposalPayload)
171+
if err != nil {
172+
logger.Errorf("VSCC error: GetChaincodeProposalPayload failed, err %s", err)
173+
return err
174+
}
175+
176+
cis := &pb.ChaincodeInvocationSpec{}
177+
err = proto.Unmarshal(cpp.Input, cis)
178+
if err != nil {
179+
logger.Errorf("VSCC error: Unmarshal ChaincodeInvocationSpec failed, err %s", err)
180+
return err
181+
}
182+
183+
if cis == nil ||
184+
cis.ChaincodeSpec == nil ||
185+
cis.ChaincodeSpec.Input == nil ||
186+
cis.ChaincodeSpec.Input.Args == nil {
187+
logger.Errorf("VSCC error: committing invalid vscc invocation")
188+
return fmt.Errorf("VSCC error: committing invalid vscc invocation")
189+
}
190+
191+
lsccFunc := string(cis.ChaincodeSpec.Input.Args[0])
192+
lsccArgs := cis.ChaincodeSpec.Input.Args[1:]
193+
194+
switch lsccFunc {
195+
case lscc.DEPLOY:
196+
case lscc.UPGRADE:
197+
logger.Infof("VSCC info: validating invocation of lscc function %s on arguments %#v", lsccFunc, lsccArgs)
198+
199+
// TODO: two more crs are expected to fill this gap, as explained in FAB-3155
200+
// 1) check that the invocation complies with the InstantiationPolicy
201+
// 2) check that the read/write set is appropriate
202+
203+
return nil
204+
default:
205+
return fmt.Errorf("VSCC error: committing an invocation of function %s of lscc is invalid", lsccFunc)
206+
}
207+
208+
return nil
209+
}

0 commit comments

Comments
 (0)