Skip to content

Commit d689f79

Browse files
author
Matthias Neugschwandtner
committed
[FAB-6526] Collection membership policy checks
Perform validation of collection configuration supplied at deploy/upgrade time to ensure that the member signature policy is meaningful (only consists of ORs). Also perform input sanitization at LSCC deploy/upgrade time to check that the member signature policy orgs are part of the channel. Change-Id: I71f82cf7757c9f360a789832effa68d0df59166a Signed-off-by: Matthias Neugschwandtner <eug@zurich.ibm.com>
1 parent aa8875d commit d689f79

File tree

5 files changed

+298
-17
lines changed

5 files changed

+298
-17
lines changed

core/handlers/validation/builtin/validation_logic.go

+32-9
Original file line numberDiff line numberDiff line change
@@ -174,34 +174,59 @@ func validateNewCollectionConfigs(newCollectionConfigs []*common.CollectionConfi
174174

175175
newCollection := newCollectionConfig.GetStaticCollectionConfig()
176176
if newCollection == nil {
177-
return policyErr(errors.New("unknown collection configuration type"))
177+
return errors.New("unknown collection configuration type")
178178
}
179179

180180
// Ensure that there are no duplicate collection names
181181
collectionName := newCollection.GetName()
182182

183183
if err := validateCollectionName(collectionName); err != nil {
184-
return policyErr(err)
184+
return err
185185
}
186186

187187
if _, ok := newCollectionsMap[collectionName]; !ok {
188188
newCollectionsMap[collectionName] = true
189189
} else {
190-
return policyErr(fmt.Errorf("collection-name: %s -- found duplicate collection configuration", collectionName))
190+
return fmt.Errorf("collection-name: %s -- found duplicate collection configuration", collectionName)
191191
}
192192

193193
// Validate gossip related parameters present in the collection config
194194
maximumPeerCount := newCollection.GetMaximumPeerCount()
195195
requiredPeerCount := newCollection.GetRequiredPeerCount()
196196
if maximumPeerCount < requiredPeerCount {
197-
return policyErr(fmt.Errorf("collection-name: %s -- maximum peer count (%d) cannot be greater than the required peer count (%d)",
198-
collectionName, maximumPeerCount, requiredPeerCount))
197+
return fmt.Errorf("collection-name: %s -- maximum peer count (%d) cannot be greater than the required peer count (%d)",
198+
collectionName, maximumPeerCount, requiredPeerCount)
199199

200200
}
201201
if requiredPeerCount < 0 {
202-
return policyErr(fmt.Errorf("collection-name: %s -- requiredPeerCount (%d) cannot be lesser than zero (%d)",
203-
collectionName, maximumPeerCount, requiredPeerCount))
202+
return fmt.Errorf("collection-name: %s -- requiredPeerCount (%d) cannot be lesser than zero (%d)",
203+
collectionName, maximumPeerCount, requiredPeerCount)
204+
205+
}
206+
207+
// make sure that the signature policy is meaningful (only consists of ORs)
208+
err := validateSpOrConcat(newCollection.MemberOrgsPolicy.GetSignaturePolicy().Rule)
209+
if err != nil {
210+
return errors.WithMessage(err, fmt.Sprintf("collection-name: %s -- error in member org policy", collectionName))
211+
}
212+
}
213+
return nil
214+
}
204215

216+
// validateSpOrConcat checks if the supplied signature policy is just an OR-concatenation of identities
217+
func validateSpOrConcat(sp *common.SignaturePolicy) error {
218+
if sp.GetNOutOf() == nil {
219+
return nil
220+
}
221+
// check if N == 1 (OR concatenation)
222+
if sp.GetNOutOf().N != 1 {
223+
return errors.New(fmt.Sprintf("signature policy is not an OR concatenation, NOutOf %d", sp.GetNOutOf().N))
224+
}
225+
// recurse into all sub-rules
226+
for _, rule := range sp.GetNOutOf().Rules {
227+
err := validateSpOrConcat(rule)
228+
if err != nil {
229+
return err
205230
}
206231
}
207232
return nil
@@ -382,8 +407,6 @@ func (vscc *ValidatorOneValidSignature) validateRWSetAndCollection(
382407
}
383408
}
384409

385-
// TODO: FAB-6526 - to add validation of the collections object
386-
387410
return nil
388411
}
389412

core/handlers/validation/builtin/validation_logic_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -1830,6 +1830,14 @@ func TestValidateRWSetAndCollectionForDeploy(t *testing.T) {
18301830
err = testValidateCollection(t, v, []*common.CollectionConfig{coll1, coll2, coll3}, cdRWSet, lsccFunc, ac, chid)
18311831
assert.Errorf(t, err, "collection-name: %s -- maximum peer count (%d) cannot be greater than the required peer count (%d)",
18321832
collName3, maximumPeerCount, requiredPeerCount)
1833+
1834+
// Test 12: AND concatenation of orgs in access policy -> error
1835+
requiredPeerCount = 1
1836+
maximumPeerCount = 2
1837+
policyEnvelope = cauthdsl.Envelope(cauthdsl.And(cauthdsl.SignedBy(0), cauthdsl.SignedBy(1)), signers)
1838+
coll3 = createCollectionConfig(collName3, policyEnvelope, requiredPeerCount, maximumPeerCount, blockToLive)
1839+
err = testValidateCollection(t, v, []*common.CollectionConfig{coll3}, cdRWSet, lsccFunc, ac, chid)
1840+
assert.EqualError(t, err, "collection-name: mycollection3 -- error in member org policy: signature policy is not an OR concatenation, NOutOf 2")
18331841
}
18341842

18351843
func TestValidateRWSetAndCollectionForUpgrade(t *testing.T) {

core/mocks/scc/lscc/support.go

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package lscc
88

99
import (
1010
"github.com/hyperledger/fabric/core/common/ccprovider"
11+
"github.com/hyperledger/fabric/protos/common"
1112
"github.com/hyperledger/fabric/protos/peer"
1213
)
1314

@@ -22,6 +23,7 @@ type MockSupport struct {
2223
CheckInstantiationPolicyErr error
2324
GetInstantiationPolicyMap map[string][]byte
2425
CheckInstantiationPolicyMap map[string]error
26+
CheckCollectionConfigErr error
2527
}
2628

2729
func (s *MockSupport) PutChaincodeToLocalStorage(ccpack ccprovider.CCPackage) error {
@@ -51,3 +53,7 @@ func (s *MockSupport) CheckInstantiationPolicy(signedProp *peer.SignedProposal,
5153
}
5254
return s.CheckInstantiationPolicyErr
5355
}
56+
57+
func (s *MockSupport) CheckCollectionConfig(collectionConfig *common.CollectionConfig, channelName string) error {
58+
return s.CheckCollectionConfigErr
59+
}

core/scc/lscc/lscc.go

+93-1
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import (
2424
"github.com/hyperledger/fabric/core/peer"
2525
"github.com/hyperledger/fabric/core/policy"
2626
"github.com/hyperledger/fabric/core/policyprovider"
27+
"github.com/hyperledger/fabric/msp"
2728
"github.com/hyperledger/fabric/msp/mgmt"
2829
"github.com/hyperledger/fabric/protos/common"
30+
mb "github.com/hyperledger/fabric/protos/msp"
2931
pb "github.com/hyperledger/fabric/protos/peer"
3032
"github.com/hyperledger/fabric/protos/utils"
3133
"github.com/pkg/errors"
@@ -142,6 +144,87 @@ func (lscc *lifeCycleSysCC) putChaincodeData(stub shim.ChaincodeStubInterface, c
142144
return err
143145
}
144146

147+
// checkCollectionMemberPolicy checks whether the supplied collection configuration
148+
// complies to the given msp configuration
149+
func checkCollectionMemberPolicy(collectionConfig *common.CollectionConfig, mspmgr msp.MSPManager) error {
150+
if mspmgr == nil {
151+
return fmt.Errorf("msp manager not set")
152+
}
153+
msps, err := mspmgr.GetMSPs()
154+
if err != nil {
155+
return errors.Wrapf(err, "error getting channel msp")
156+
}
157+
if collectionConfig == nil {
158+
return fmt.Errorf("collection configuration is not set")
159+
}
160+
coll := collectionConfig.GetStaticCollectionConfig()
161+
if coll == nil {
162+
return fmt.Errorf("collection configuration is empty")
163+
}
164+
if coll.MemberOrgsPolicy == nil {
165+
return fmt.Errorf("collection member policy is not set")
166+
}
167+
if coll.MemberOrgsPolicy.GetSignaturePolicy() == nil {
168+
return fmt.Errorf("collection member org policy is empty")
169+
}
170+
// make sure that the orgs listed are actually part of the channel
171+
// check all principals in the signature policy
172+
for _, principal := range coll.MemberOrgsPolicy.GetSignaturePolicy().Identities {
173+
found := false
174+
var orgID string
175+
// the member org policy only supports certain principal types
176+
switch principal.PrincipalClassification {
177+
178+
case mb.MSPPrincipal_ROLE:
179+
msprole := &mb.MSPRole{}
180+
err := proto.Unmarshal(principal.Principal, msprole)
181+
if err != nil {
182+
return errors.Wrapf(err, "collection-name: %s -- cannot unmarshal identities", coll.GetName())
183+
}
184+
orgID = msprole.MspIdentifier
185+
// the msp map is indexed using msp IDs - this behavior is implementation specific, making the following check a bit of a hack
186+
for mspid := range msps {
187+
if mspid == orgID {
188+
found = true
189+
break
190+
}
191+
}
192+
193+
case mb.MSPPrincipal_ORGANIZATION_UNIT:
194+
mspou := &mb.OrganizationUnit{}
195+
err := proto.Unmarshal(principal.Principal, mspou)
196+
if err != nil {
197+
return errors.Wrapf(err, "collection-name: %s -- cannot unmarshal identities", coll.GetName())
198+
}
199+
orgID = mspou.MspIdentifier
200+
// the msp map is indexed using msp IDs - this behavior is implementation specific, making the following check a bit of a hack
201+
for mspid := range msps {
202+
if mspid == orgID {
203+
found = true
204+
break
205+
}
206+
}
207+
208+
case mb.MSPPrincipal_IDENTITY:
209+
orgID = "identity principal"
210+
for _, msp := range msps {
211+
_, err := msp.DeserializeIdentity(principal.Principal)
212+
if err == nil {
213+
found = true
214+
break
215+
}
216+
}
217+
default:
218+
return fmt.Errorf("collection-name: %s -- principal type %v is not supported", coll.GetName(), principal.PrincipalClassification)
219+
}
220+
if !found {
221+
logger.Warningf("collection-name: %s collection member %s is not part of the channel", coll.GetName(), orgID)
222+
}
223+
}
224+
225+
return nil
226+
}
227+
145228
// putChaincodeCollectionData adds collection data for the chaincode
146229
func (lscc *lifeCycleSysCC) putChaincodeCollectionData(stub shim.ChaincodeStubInterface, cd *ccprovider.ChaincodeData, collectionConfigBytes []byte) error {
147230
if cd == nil {
@@ -159,7 +242,16 @@ func (lscc *lifeCycleSysCC) putChaincodeCollectionData(stub shim.ChaincodeStubIn
159242
return errors.Errorf("invalid collection configuration supplied for chaincode %s:%s", cd.Name, cd.Version)
160243
}
161244

162-
// TODO: FAB-6526 - to add validation of the collections object
245+
mspmgr := mgmt.GetManagerForChain(stub.GetChannelID())
246+
if mspmgr == nil {
247+
return fmt.Errorf("could not get MSP manager for channel %s", stub.GetChannelID())
248+
}
249+
for _, collectionConfig := range collections.Config {
250+
err = checkCollectionMemberPolicy(collectionConfig, mspmgr)
251+
if err != nil {
252+
return errors.Wrapf(err, "collection member policy check failed")
253+
}
254+
}
163255

164256
key := privdata.BuildCollectionKVSKey(cd.Name)
165257

0 commit comments

Comments
 (0)