Skip to content

Commit

Permalink
[FAB-1302] (PA) Add channel create authorization
Browse files Browse the repository at this point in the history
There is currently no inspection of the channel creation transaction.
This means that during a channel creation, the creator could
inappropriately specify config, including items like orderer parameters,
and the ordering service would silently accept this incorrect
parameters.  This is especially troublesome from a security perspective,
as a specially crafted config tx could potentially trick another user
into believe they were participating in a channel with one member when
they are actually participating with another.

This CR adds this checking by leveraging the existing underlying
authorization mechanisms of the configuration transaction.  It ensures
that the creating members are not only authorized to create the channel,
but also ensures that all channel config that is set is done so in
accordance with the underlying governance for that config element.  This
means that a collection of authorized ordering organizations may choose
to modifying ordering parameters in a channel creation request while a
set of application orgs would not be able to.

This CR is somewhat large due to the significant invasive nature of this
change, and that the entire end to end must be modified, not just a
single component.  However, given the increase in function with decrease
in complexity, the diff stat is hopefully acceptable.

Change-Id: I9bb64d3f70d07eb8a8a4ea4893c71cf1112b4bf7
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
Signed-off-by: Kostas Christidis <kostas@christidis.io>
  • Loading branch information
Jason Yellick authored and kchristidis committed Apr 26, 2017
1 parent 8288a7f commit 312628c
Show file tree
Hide file tree
Showing 23 changed files with 642 additions and 366 deletions.
19 changes: 11 additions & 8 deletions common/cauthdsl/cauthdsl.go
Expand Up @@ -41,7 +41,7 @@ func compile(policy *cb.SignaturePolicy, identities []*mb.MSPPrincipal, deserial

}
return func(signedData []*cb.SignedData, used []bool) bool {
cauthdslLogger.Debugf("Gate evaluation starts: (%s)", t)
cauthdslLogger.Debugf("Gate evaluation starts: (%v)", t)
verified := int32(0)
_used := make([]bool, len(used))
for _, policy := range policies {
Expand All @@ -53,20 +53,20 @@ func compile(policy *cb.SignaturePolicy, identities []*mb.MSPPrincipal, deserial
}

if verified >= t.NOutOf.N {
cauthdslLogger.Debugf("Gate evaluation succeeds: (%s)", t)
cauthdslLogger.Debugf("Gate evaluation succeeds: (%v)", t)
} else {
cauthdslLogger.Debugf("Gate evaluation fails: (%s)", t)
cauthdslLogger.Debugf("Gate evaluation fails: (%v)", t)
}

return verified >= t.NOutOf.N
}, nil
case *cb.SignaturePolicy_SignedBy:
if t.SignedBy < 0 || t.SignedBy >= int32(len(identities)) {
return nil, fmt.Errorf("Identity index out of range, requested %d, but identies length is %d", t.SignedBy, len(identities))
return nil, fmt.Errorf("Identity index out of range, requested %v, but identies length is %d", t.SignedBy, len(identities))
}
signedByID := identities[t.SignedBy]
return func(signedData []*cb.SignedData, used []bool) bool {
cauthdslLogger.Debugf("Principal evaluation starts: (%s) (used %s)", t, used)
cauthdslLogger.Debugf("Principal evaluation starts: (%v) (used %v)", t, used)
for i, sd := range signedData {
if used[i] {
continue
Expand All @@ -78,15 +78,18 @@ func compile(policy *cb.SignaturePolicy, identities []*mb.MSPPrincipal, deserial
}
err = identity.SatisfiesPrincipal(signedByID)
if err == nil {
err := identity.Verify(sd.Data, sd.Signature)
cauthdslLogger.Debugf("Principal matched by identity: (%v) for %v", t, sd.Identity)
err = identity.Verify(sd.Data, sd.Signature)
if err == nil {
cauthdslLogger.Debugf("Principal evaluation succeeds: (%s) (used %s)", t, used)
cauthdslLogger.Debugf("Principal evaluation succeeds: (%v) (used %v)", t, used)
used[i] = true
return true
}
} else {
cauthdslLogger.Debugf("Identity (%v) does not satisfy principal: %s", sd.Identity, err)
}
}
cauthdslLogger.Debugf("Principal evaluation fails: (%s)", t, used)
cauthdslLogger.Debugf("Principal evaluation fails: (%v) %v", t, used)
return false
}, nil
default:
Expand Down
52 changes: 38 additions & 14 deletions common/configtx/template.go
Expand Up @@ -20,10 +20,11 @@ import (
"fmt"

"github.com/hyperledger/fabric/common/config"
configmsp "github.com/hyperledger/fabric/common/config/msp"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/msp"
cb "github.com/hyperledger/fabric/protos/common"
ab "github.com/hyperledger/fabric/protos/orderer"
"github.com/hyperledger/fabric/protos/utils"

"github.com/golang/protobuf/proto"
Expand Down Expand Up @@ -195,28 +196,51 @@ type channelCreationTemplate struct {
orgs []string
}

// NewChainCreationTemplate takes a CreationPolicy and a Template to produce a
// NewChainCreationTemplate takes a consortium name and a Template to produce a
// Template which outputs an appropriately constructed list of ConfigUpdateEnvelopes.
func NewChainCreationTemplate(creationPolicy string, template Template) Template {
result := cb.NewConfigGroup()
result.Groups[config.OrdererGroupKey] = cb.NewConfigGroup()
result.Groups[config.OrdererGroupKey].Values[CreationPolicyKey] = &cb.ConfigValue{
Value: utils.MarshalOrPanic(&ab.CreationPolicy{
Policy: creationPolicy,
}),
func NewChainCreationTemplate(consortiumName string, orgs []string) Template {
return &channelCreationTemplate{
consortiumName: consortiumName,
orgs: orgs,
}
}

func (cct *channelCreationTemplate) Envelope(channelID string) (*cb.ConfigUpdateEnvelope, error) {
rSet := config.TemplateConsortium(cct.consortiumName)
wSet := config.TemplateConsortium(cct.consortiumName)

rSet.Groups[config.ApplicationGroupKey] = cb.NewConfigGroup()
wSet.Groups[config.ApplicationGroupKey] = cb.NewConfigGroup()

for _, org := range cct.orgs {
rSet.Groups[config.ApplicationGroupKey].Groups[org] = cb.NewConfigGroup()
wSet.Groups[config.ApplicationGroupKey].Groups[org] = cb.NewConfigGroup()
}
return NewCompositeTemplate(NewSimpleTemplate(result), template)

wSet.Groups[config.ApplicationGroupKey].ModPolicy = configmsp.AdminsPolicyKey
wSet.Groups[config.ApplicationGroupKey].Policies[configmsp.AdminsPolicyKey] = policies.ImplicitMetaPolicyWithSubPolicy(configmsp.AdminsPolicyKey, cb.ImplicitMetaPolicy_MAJORITY)
wSet.Groups[config.ApplicationGroupKey].Policies[configmsp.WritersPolicyKey] = policies.ImplicitMetaPolicyWithSubPolicy(configmsp.WritersPolicyKey, cb.ImplicitMetaPolicy_ANY)
wSet.Groups[config.ApplicationGroupKey].Policies[configmsp.ReadersPolicyKey] = policies.ImplicitMetaPolicyWithSubPolicy(configmsp.ReadersPolicyKey, cb.ImplicitMetaPolicy_ANY)
wSet.Groups[config.ApplicationGroupKey].Version = 1

return &cb.ConfigUpdateEnvelope{
ConfigUpdate: utils.MarshalOrPanic(&cb.ConfigUpdate{
ChannelId: channelID,
ReadSet: rSet,
WriteSet: wSet,
}),
}, nil
}

// MakeChainCreationTransaction is a handy utility function for creating new chain transactions using the underlying Template framework
func MakeChainCreationTransaction(creationPolicy string, chainID string, signer msp.SigningIdentity, templates ...Template) (*cb.Envelope, error) {
func MakeChainCreationTransaction(channelID string, consortium string, signer msp.SigningIdentity, orgs ...string) (*cb.Envelope, error) {
sSigner, err := signer.Serialize()
if err != nil {
return nil, fmt.Errorf("Serialization of identity failed, err %s", err)
}

newChainTemplate := NewChainCreationTemplate(creationPolicy, NewCompositeTemplate(templates...))
newConfigUpdateEnv, err := newChainTemplate.Envelope(chainID)
newChainTemplate := NewChainCreationTemplate(consortium, orgs)
newConfigUpdateEnv, err := newChainTemplate.Envelope(channelID)
if err != nil {
return nil, err
}
Expand All @@ -229,7 +253,7 @@ func MakeChainCreationTransaction(creationPolicy string, chainID string, signer
return nil, err
}

payloadChannelHeader := utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, msgVersion, chainID, epoch)
payloadChannelHeader := utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, msgVersion, channelID, epoch)
payloadSignatureHeader := utils.MakeSignatureHeader(sSigner, utils.CreateNonceOrPanic())
utils.SetTxID(payloadChannelHeader, payloadSignatureHeader)
payloadHeader := utils.MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader)
Expand Down
35 changes: 14 additions & 21 deletions common/configtx/template_test.go
Expand Up @@ -20,11 +20,10 @@ import (
"fmt"
"testing"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/config"
cb "github.com/hyperledger/fabric/protos/common"
ab "github.com/hyperledger/fabric/protos/orderer"

"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -105,38 +104,32 @@ func TestModPolicySettingTemplate(t *testing.T) {
}

func TestNewChainTemplate(t *testing.T) {
simple := NewSimpleTemplate(
simpleGroup(0),
simpleGroup(1),
)

creationPolicy := "Test"
nct := NewChainCreationTemplate(creationPolicy, simple)
consortiumName := "Test"
orgs := []string{"org1", "org2", "org3"}
nct := NewChainCreationTemplate(consortiumName, orgs)

newChainID := "foo"
configEnv, err := nct.Envelope(newChainID)
if err != nil {
t.Fatalf("Error creation a chain creation config")
}

configNext, err := UnmarshalConfigUpdate(configEnv.ConfigUpdate)
configUpdate, err := UnmarshalConfigUpdate(configEnv.ConfigUpdate)
if err != nil {
t.Fatalf("Should not have errored: %s", err)
}

assert.Equal(t, len(configNext.WriteSet.Values), 2, "Not the right number of config values")
consortiumProto := &cb.Consortium{}
err = proto.Unmarshal(configUpdate.WriteSet.Values[config.ConsortiumKey].Value, consortiumProto)
assert.NoError(t, err)
assert.Equal(t, consortiumName, consortiumProto.Name, "Should have set correct consortium name")

for i := 0; i < 2; i++ {
_, ok := configNext.WriteSet.Values[fmt.Sprintf("%d", i)]
assert.True(t, ok, "Expected to find %d but did not", i)
}
assert.Equal(t, configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Version, uint64(1))

configValue, ok := configNext.WriteSet.Groups[config.OrdererGroupKey].Values[CreationPolicyKey]
assert.True(t, ok, "Did not find creation policy")
assert.Len(t, configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups, len(orgs))

creationPolicyMessage := new(ab.CreationPolicy)
if err := proto.Unmarshal(configValue.Value, creationPolicyMessage); err != nil {
t.Fatal("Should not have errored:", err)
for _, org := range orgs {
_, ok := configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org]
assert.True(t, ok, "Expected to find %s but did not", org)
}
assert.Equal(t, creationPolicy, creationPolicyMessage.Policy, "Policy names don't match")
}

0 comments on commit 312628c

Please sign in to comment.