// WalkTargets will apply the specified visitor function to iteratively walk the targets/delegation metadata tree, // until receiving a StopWalk. The walk starts from the base "targets" role, and searches for the correct targetPath and/or rolePath // to call the visitor function on. Any roles passed into skipRoles will be excluded from the walk, as well as roles in those subtrees func (tr *Repo) WalkTargets(targetPath, rolePath string, visitTargets walkVisitorFunc, skipRoles ...string) error { // Start with the base targets role, which implicitly has the "" targets path targetsRole, err := tr.GetBaseRole(data.CanonicalTargetsRole) if err != nil { return err } // Make the targets role have the empty path, when we treat it as a delegation role roles := []data.DelegationRole{ { BaseRole: targetsRole, Paths: []string{""}, }, } for len(roles) > 0 { role := roles[0] roles = roles[1:] // Check the role metadata signedTgt, ok := tr.Targets[role.Name] if !ok { // The role meta doesn't exist in the repo so continue onward continue } // We're at a prefix of the desired role subtree, so add its delegation role children and continue walking if strings.HasPrefix(rolePath, role.Name+"/") { roles = append(roles, signedTgt.GetValidDelegations(role)...) continue } // Determine whether to visit this role or not: // If the paths validate against the specified targetPath and the rolePath is empty or is in the subtree // Also check if we are choosing to skip visiting this role on this walk (see ListTargets and GetTargetByName priority) if isValidPath(targetPath, role) && isAncestorRole(role.Name, rolePath) && !utils.StrSliceContains(skipRoles, role.Name) { // If we had matching path or role name, visit this target and determine whether or not to keep walking res := visitTargets(signedTgt, role) switch typedRes := res.(type) { case StopWalk: // If the visitor function signalled a stop, return nil to finish the walk return nil case nil: // If the visitor function signalled to continue, add this role's delegation to the walk roles = append(roles, signedTgt.GetValidDelegations(role)...) case error: // Propagate any errors from the visitor return typedRes default: // Return out with an error if we got a different result return fmt.Errorf("unexpected return while walking: %v", res) } } } return nil }
func (t trustPinChecker) certsCheck(leafCert *x509.Certificate, intCerts []*x509.Certificate) bool { // reconstruct the leaf + intermediate cert chain, which is bundled as {leaf, intermediates...}, // in order to get the matching id in the root file key, err := trustmanager.CertBundleToKey(leafCert, intCerts) if err != nil { logrus.Debug("error creating cert bundle: ", err.Error()) return false } return utils.StrSliceContains(t.pinnedCertIDs, key.ID()) }
// UpdateDelegations updates the appropriate delegations, either adding // a new delegation or updating an existing one. If keys are // provided, the IDs will be added to the role (if they do not exist // there already), and the keys will be added to the targets file. func (tr *Repo) UpdateDelegations(role *data.Role, keys []data.PublicKey) error { if !role.IsDelegation() || !role.IsValid() { return data.ErrInvalidRole{Role: role.Name, Reason: "not a valid delegated role"} } parent := path.Dir(role.Name) if err := tr.VerifyCanSign(parent); err != nil { return err } // check the parent role's metadata p, ok := tr.Targets[parent] if !ok { // the parent targetfile may not exist yet - if not, then create it var err error p, err = tr.InitTargets(parent) if err != nil { return err } } for _, k := range keys { if !utils.StrSliceContains(role.KeyIDs, k.ID()) { role.KeyIDs = append(role.KeyIDs, k.ID()) } p.Signed.Delegations.Keys[k.ID()] = k tr.keysDB.AddKey(k) } // if the role has fewer keys than the threshold, it // will never be able to create a valid targets file // and should be considered invalid. if len(role.KeyIDs) < role.Threshold { return data.ErrInvalidRole{Role: role.Name, Reason: "insufficient keys to meet threshold"} } foundAt := utils.FindRoleIndex(p.Signed.Delegations.Roles, role.Name) if foundAt >= 0 { p.Signed.Delegations.Roles[foundAt] = role } else { p.Signed.Delegations.Roles = append(p.Signed.Delegations.Roles, role) } // We've made a change to parent. Set it to dirty p.Dirty = true // We don't actually want to create the new delegation metadata yet. // When we add a delegation, it may only be signable by a key we don't have // (hence we are delegating signing). tr.keysDB.AddRole(role) utils.RemoveUnusedKeys(p) return nil }
// CheckHealth verifies that DB exists and is query-able func (rdb RethinkDBKeyStore) CheckHealth() error { var tables []string dbPrivateKey := RDBPrivateKey{} res, err := gorethink.DB(rdb.dbName).TableList().Run(rdb.sess) if err != nil { return err } defer res.Close() err = res.All(&tables) if err != nil || !utils.StrSliceContains(tables, dbPrivateKey.TableName()) { return fmt.Errorf( "Cannot access table: %s", dbPrivateKey.TableName()) } return nil }
func (t trustPinChecker) certsCheck(leafCert *x509.Certificate, intCerts []*x509.Certificate) bool { // reconstruct the leaf + intermediate cert chain, which is bundled as {leaf, intermediates...}, // in order to get the matching id in the root file leafCertID, err := trustmanager.FingerprintCert(leafCert) if err != nil { return false } rootKeys := trustmanager.CertsToKeys([]*x509.Certificate{leafCert}, map[string][]*x509.Certificate{leafCertID: intCerts}) for keyID := range rootKeys { if utils.StrSliceContains(t.pinnedCertIDs, keyID) { return true } } return false }
// UpdateDelegations updates the appropriate delegations, either adding // a new delegation or updating an existing one. If keys are // provided, the IDs will be added to the role (if they do not exist // there already), and the keys will be added to the targets file. // The "before" argument specifies another role which this new role // will be added in front of (i.e. higher priority) in the delegation list. // An empty before string indicates to add the role to the end of the // delegation list. // A new, empty, targets file will be created for the new role. func (tr *Repo) UpdateDelegations(role *data.Role, keys []data.PublicKey, before string) error { if !role.IsDelegation() || !role.IsValid() { return data.ErrInvalidRole{Role: role.Name} } parent := filepath.Dir(role.Name) p, ok := tr.Targets[parent] if !ok { return data.ErrInvalidRole{Role: role.Name} } for _, k := range keys { if !utils.StrSliceContains(role.KeyIDs, k.ID()) { role.KeyIDs = append(role.KeyIDs, k.ID()) } p.Signed.Delegations.Keys[k.ID()] = k tr.keysDB.AddKey(k) } i := -1 var r *data.Role for i, r = range p.Signed.Delegations.Roles { if r.Name == role.Name { break } } if i >= 0 { p.Signed.Delegations.Roles[i] = role } else { p.Signed.Delegations.Roles = append(p.Signed.Delegations.Roles, role) } p.Dirty = true roleTargets := data.NewTargets() // NewTargets always marked Dirty tr.Targets[role.Name] = roleTargets tr.keysDB.AddRole(role) return nil }
// Walk to parent, and either create or update this delegation. We can only create a new delegation if we're given keys // Ensure all updates are valid, by checking against parent ancestor paths and ensuring the keys meet the role threshold. func delegationUpdateVisitor(roleName string, addKeys data.KeyList, removeKeys, addPaths, removePaths []string, clearAllPaths bool, newThreshold int) walkVisitorFunc { return func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} { var err error // Validate the changes underneath this restricted validRole for adding paths, reject invalid path additions if len(addPaths) != len(data.RestrictDelegationPathPrefixes(validRole.Paths, addPaths)) { return data.ErrInvalidRole{Role: roleName, Reason: "invalid paths to add to role"} } // Try to find the delegation and amend it using our changelist var delgRole *data.Role for _, role := range tgt.Signed.Delegations.Roles { if role.Name == roleName { // Make a copy and operate on this role until we validate the changes keyIDCopy := make([]string, len(role.KeyIDs)) copy(keyIDCopy, role.KeyIDs) pathsCopy := make([]string, len(role.Paths)) copy(pathsCopy, role.Paths) delgRole = &data.Role{ RootRole: data.RootRole{ KeyIDs: keyIDCopy, Threshold: role.Threshold, }, Name: role.Name, Paths: pathsCopy, } delgRole.RemovePaths(removePaths) if clearAllPaths { delgRole.Paths = []string{} } delgRole.AddPaths(addPaths) delgRole.RemoveKeys(removeKeys) break } } // We didn't find the role earlier, so create it only if we have keys to add if delgRole == nil { if len(addKeys) > 0 { delgRole, err = data.NewRole(roleName, newThreshold, addKeys.IDs(), addPaths) if err != nil { return err } } else { // If we can't find the role and didn't specify keys to add, this is an error return data.ErrInvalidRole{Role: roleName, Reason: "cannot create new delegation without keys"} } } // Add the key IDs to the role and the keys themselves to the parent for _, k := range addKeys { if !utils.StrSliceContains(delgRole.KeyIDs, k.ID()) { delgRole.KeyIDs = append(delgRole.KeyIDs, k.ID()) } } // Make sure we have a valid role still if len(delgRole.KeyIDs) < delgRole.Threshold { return data.ErrInvalidRole{Role: roleName, Reason: "insufficient keys to meet threshold"} } // NOTE: this closure CANNOT error after this point, as we've committed to editing the SignedTargets metadata in the repo object. // Any errors related to updating this delegation must occur before this point. // If all of our changes were valid, we should edit the actual SignedTargets to match our copy for _, k := range addKeys { tgt.Signed.Delegations.Keys[k.ID()] = k } foundAt := utils.FindRoleIndex(tgt.Signed.Delegations.Roles, delgRole.Name) if foundAt < 0 { tgt.Signed.Delegations.Roles = append(tgt.Signed.Delegations.Roles, delgRole) } else { tgt.Signed.Delegations.Roles[foundAt] = delgRole } tgt.Dirty = true utils.RemoveUnusedKeys(tgt) return StopWalk{} } }
// Reads the configuration file for storage setup, and sets up the cryptoservice // mapping func setUpCryptoservices(configuration *viper.Viper, allowedBackends []string, doBootstrap bool) ( signer.CryptoServiceIndex, error) { backend := configuration.GetString("storage.backend") if !tufutils.StrSliceContains(allowedBackends, backend) { return nil, fmt.Errorf("%s is not an allowed backend, must be one of: %s", backend, allowedBackends) } var keyService signed.CryptoService switch backend { case notary.MemoryBackend: keyService = cryptoservice.NewCryptoService(trustmanager.NewKeyMemoryStore( passphrase.ConstantRetriever("memory-db-ignore"))) case notary.RethinkDBBackend: var sess *gorethink.Session storeConfig, err := utils.ParseRethinkDBStorage(configuration) if err != nil { return nil, err } defaultAlias, err := getDefaultAlias(configuration) if err != nil { return nil, err } tlsOpts := tlsconfig.Options{ CAFile: storeConfig.CA, CertFile: storeConfig.Cert, KeyFile: storeConfig.Key, } if doBootstrap { sess, err = rethinkdb.AdminConnection(tlsOpts, storeConfig.Source) } else { sess, err = rethinkdb.UserConnection(tlsOpts, storeConfig.Source, storeConfig.Username, storeConfig.Password) } if err != nil { return nil, fmt.Errorf("Error starting %s driver: %s", backend, err.Error()) } s := keydbstore.NewRethinkDBKeyStore(storeConfig.DBName, storeConfig.Username, storeConfig.Password, passphraseRetriever, defaultAlias, sess) health.RegisterPeriodicFunc("DB operational", time.Minute, s.CheckHealth) if doBootstrap { keyService = s } else { keyService = keydbstore.NewCachedKeyService(s) } case notary.MySQLBackend, notary.SQLiteBackend, notary.PostgresBackend: storeConfig, err := utils.ParseSQLStorage(configuration) if err != nil { return nil, err } defaultAlias, err := getDefaultAlias(configuration) if err != nil { return nil, err } dbStore, err := keydbstore.NewSQLKeyDBStore( passphraseRetriever, defaultAlias, storeConfig.Backend, storeConfig.Source) if err != nil { return nil, fmt.Errorf("failed to create a new keydbstore: %v", err) } health.RegisterPeriodicFunc( "DB operational", time.Minute, dbStore.HealthCheck) keyService = keydbstore.NewCachedKeyService(dbStore) } if doBootstrap { err := bootstrap(keyService) if err != nil { logrus.Fatal(err.Error()) } os.Exit(0) } cryptoServices := make(signer.CryptoServiceIndex) cryptoServices[data.ED25519Key] = keyService cryptoServices[data.ECDSAKey] = keyService return cryptoServices, nil }