When signing the root, modify and sign a temporary root that gets assigned back into

the repo if signing was successful.  This way, we don't mutate the existing root
in a failed attempt to sign it.

Signed-off-by: Ying Li <ying.li@docker.com>
This commit is contained in:
Ying Li 2016-04-13 22:25:43 -07:00
parent cea46f7c3e
commit f8cd53cf2f
1 changed files with 30 additions and 22 deletions

View File

@ -842,12 +842,23 @@ func (v versionedRootRoles) Less(i, j int) bool { return v[i].version < v[j].ver
// back to the way it was (so version won't be incremented, for instance). // back to the way it was (so version won't be incremented, for instance).
func (tr *Repo) SignRoot(expires time.Time) (*data.Signed, error) { func (tr *Repo) SignRoot(expires time.Time) (*data.Signed, error) {
logrus.Debug("signing root...") logrus.Debug("signing root...")
// duplicate root and attempt to modify it rather than the existing root
rootBytes, err := tr.Root.MarshalJSON()
if err != nil {
return nil, err
}
tempRoot := data.SignedRoot{}
if err := json.Unmarshal(rootBytes, &tempRoot); err != nil {
return nil, err
}
currRoot, err := tr.GetBaseRole(data.CanonicalRootRole) currRoot, err := tr.GetBaseRole(data.CanonicalRootRole)
if err != nil { if err != nil {
return nil, err return nil, err
} }
oldRootRoles, origRoles := tr.getOldRootRoles() oldRootRoles := tr.getOldRootRoles()
var latestSavedRole data.BaseRole var latestSavedRole data.BaseRole
rolesToSignWith := make([]data.BaseRole, 0, len(oldRootRoles)) rolesToSignWith := make([]data.BaseRole, 0, len(oldRootRoles))
@ -867,49 +878,48 @@ func (tr *Repo) SignRoot(expires time.Time) (*data.Signed, error) {
// if the root role has changed and original role had not been saved as a previous role, save it now // if the root role has changed and original role had not been saved as a previous role, save it now
if !tr.originalRootRole.Equals(currRoot) && !tr.originalRootRole.Equals(latestSavedRole) { if !tr.originalRootRole.Equals(currRoot) && !tr.originalRootRole.Equals(latestSavedRole) {
tr.saveOldRootRole(tr.originalRootRole, tr.Root.Signed.Version)
rolesToSignWith = append(rolesToSignWith, tr.originalRootRole) rolesToSignWith = append(rolesToSignWith, tr.originalRootRole)
latestSavedRole = tr.originalRootRole latestSavedRole = tr.originalRootRole
versionName := oldRootVersionName(tempRoot.Signed.Version)
tempRoot.Signed.Roles[versionName] = &data.RootRole{
KeyIDs: latestSavedRole.ListKeyIDs(), Threshold: latestSavedRole.Threshold}
} }
origVersion := tr.Root.Signed.Expires tempRoot.Signed.Expires = expires
tr.Root.Signed.Expires = expires tempRoot.Signed.Version++
tr.Root.Signed.Version++
// if the current role doesn't match with the latest saved role, save it // if the current role doesn't match with the latest saved role, save it
if !currRoot.Equals(latestSavedRole) { if !currRoot.Equals(latestSavedRole) {
tr.saveOldRootRole(currRoot, tr.Root.Signed.Version)
rolesToSignWith = append(rolesToSignWith, currRoot) rolesToSignWith = append(rolesToSignWith, currRoot)
versionName := oldRootVersionName(tempRoot.Signed.Version)
tempRoot.Signed.Roles[versionName] = &data.RootRole{
KeyIDs: currRoot.ListKeyIDs(), Threshold: currRoot.Threshold}
} }
signed, err := tr.Root.ToSigned() signed, err := tempRoot.ToSigned()
if err != nil { if err != nil {
tr.Root.Signed.Expires = origVersion
tr.Root.Signed.Version--
tr.Root.Signed.Roles = origRoles
return nil, err return nil, err
} }
signed, err = tr.sign(signed, rolesToSignWith, tr.getOptionalRootKeys(rolesToSignWith)) signed, err = tr.sign(signed, rolesToSignWith, tr.getOptionalRootKeys(rolesToSignWith))
if err != nil { if err != nil {
tr.Root.Signed.Expires = origVersion
tr.Root.Signed.Version--
tr.Root.Signed.Roles = origRoles
return nil, err return nil, err
} }
tr.Root = &tempRoot
tr.Root.Signatures = signed.Signatures tr.Root.Signatures = signed.Signatures
tr.originalRootRole = currRoot tr.originalRootRole = currRoot
return signed, nil return signed, nil
} }
// get all the saved previous roles <= the current root version // get all the saved previous roles <= the current root version
func (tr *Repo) getOldRootRoles() (versionedRootRoles, map[string]*data.RootRole) { func (tr *Repo) getOldRootRoles() versionedRootRoles {
oldRootRoles := make(versionedRootRoles, 0, len(tr.Root.Signed.Roles)) oldRootRoles := make(versionedRootRoles, 0, len(tr.Root.Signed.Roles))
oldRoles := make(map[string]*data.RootRole)
// now go through the old roles // now go through the old roles
for roleName, r := range tr.Root.Signed.Roles { for roleName := range tr.Root.Signed.Roles {
oldRoles[roleName] = r
// ensure that the rolename matches our format and that the version is // ensure that the rolename matches our format and that the version is
// not too high // not too high
if data.ValidRole(roleName) { if data.ValidRole(roleName) {
@ -933,7 +943,7 @@ func (tr *Repo) getOldRootRoles() (versionedRootRoles, map[string]*data.RootRole
oldRootRoles = append(oldRootRoles, versionedRootRole{BaseRole: oldRole, version: version}) oldRootRoles = append(oldRootRoles, versionedRootRole{BaseRole: oldRole, version: version})
} }
return oldRootRoles, oldRoles return oldRootRoles
} }
// gets any extra optional root keys from the existing root.json signatures // gets any extra optional root keys from the existing root.json signatures
@ -960,10 +970,8 @@ func (tr *Repo) getOptionalRootKeys(signingRoles []data.BaseRole) []data.PublicK
return oldKeys return oldKeys
} }
func (tr *Repo) saveOldRootRole(role data.BaseRole, version int) { func oldRootVersionName(version int) string {
versionName := fmt.Sprintf("%s.%v", data.CanonicalRootRole, version) return fmt.Sprintf("%s.%v", data.CanonicalRootRole, version)
tr.Root.Signed.Roles[versionName] = &data.RootRole{
KeyIDs: role.ListKeyIDs(), Threshold: role.Threshold}
} }
// SignTargets signs the targets file for the given top level or delegated targets role // SignTargets signs the targets file for the given top level or delegated targets role