Example #1
0
// 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
}
Example #2
0
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())
}
Example #3
0
// 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
}
Example #4
0
// 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
}
Example #5
0
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
}
Example #6
0
// 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
}
Example #7
0
// 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{}
	}
}
Example #8
0
// 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
}