Example #1
0
func delegationAddInput(d *delegationCommander, cmd *cobra.Command, args []string) (
	config *viper.Viper, gun string, role string, keyIDs []string, error error) {
	if len(args) < 2 {
		cmd.Usage()
		return nil, "", "", nil, fmt.Errorf("must specify the Global Unique Name and the role of the delegation along with optional keyIDs and/or a list of paths to remove")
	}

	config, err := d.configGetter()
	if err != nil {
		return nil, "", "", nil, err
	}

	gun = args[0]
	role = args[1]
	// Check if role is valid delegation name before requiring any user input
	if !data.IsDelegation(role) {
		return nil, "", "", nil, fmt.Errorf("invalid delegation name %s", role)
	}

	// If we're only given the gun and the role, attempt to remove all data for this delegation
	if len(args) == 2 && d.paths == nil && !d.allPaths {
		d.removeAll = true
	}

	if len(args) > 2 {
		keyIDs = args[2:]
	}

	// If the user passes --all-paths, don't use any of the passed in --paths
	if d.allPaths {
		d.paths = nil
	}

	return config, gun, role, keyIDs, nil
}
Example #2
0
// AddDelegationRoleAndKeys creates a changelist entry to add provided delegation public keys.
// This method is the simplest way to create a new delegation, because the delegation must have at least
// one key upon creation to be valid since we will reject the changelist while validating the threshold.
func (r *NotaryRepository) AddDelegationRoleAndKeys(name string, delegationKeys []data.PublicKey) error {

	if !data.IsDelegation(name) {
		return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
	}

	cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist"))
	if err != nil {
		return err
	}
	defer cl.Close()

	logrus.Debugf(`Adding delegation "%s" with threshold %d, and %d keys\n`,
		name, notary.MinThreshold, len(delegationKeys))

	// Defaulting to threshold of 1, since we don't allow for larger thresholds at the moment.
	tdJSON, err := json.Marshal(&changelist.TufDelegation{
		NewThreshold: notary.MinThreshold,
		AddKeys:      data.KeyList(delegationKeys),
	})
	if err != nil {
		return err
	}

	template := newCreateDelegationChange(name, tdJSON)
	return addChange(cl, template, name)
}
Example #3
0
// UpdateDelegationKeys 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) UpdateDelegationKeys(roleName string, addKeys data.KeyList, removeKeys []string, newThreshold int) error {
	if !data.IsDelegation(roleName) {
		return data.ErrInvalidRole{Role: roleName, Reason: "not a valid delegated role"}
	}
	parent := path.Dir(roleName)

	if err := tr.VerifyCanSign(parent); err != nil {
		return err
	}

	// check the parent role's metadata
	_, ok := tr.Targets[parent]
	if !ok { // the parent targetfile may not exist yet - if not, then create it
		var err error
		_, err = tr.InitTargets(parent)
		if err != nil {
			return err
		}
	}

	// Walk to the parent of this delegation, since that is where its role metadata exists
	// We do not have to verify that the walker reached its desired role in this scenario
	// since we've already done another walk to the parent role in VerifyCanSign, and potentially made a targets file
	err := tr.WalkTargets("", parent, delegationUpdateVisitor(roleName, addKeys, removeKeys, []string{}, []string{}, false, newThreshold))
	if err != nil {
		return err
	}
	return nil
}
Example #4
0
// VerifyCanSign returns nil if the role exists and we have at least one
// signing key for the role, false otherwise.  This does not check that we have
// enough signing keys to meet the threshold, since we want to support the use
// case of multiple signers for a role.  It returns an error if the role doesn't
// exist or if there are no signing keys.
func (tr *Repo) VerifyCanSign(roleName string) error {
	var (
		role data.BaseRole
		err  error
	)
	// we only need the BaseRole part of a delegation because we're just
	// checking KeyIDs
	if data.IsDelegation(roleName) {
		r, err := tr.GetDelegationRole(roleName)
		if err != nil {
			return err
		}
		role = r.BaseRole
	} else {
		role, err = tr.GetBaseRole(roleName)
	}
	if err != nil {
		return data.ErrInvalidRole{Role: roleName, Reason: "does not exist"}
	}

	for keyID, k := range role.Keys {
		check := []string{keyID}
		if canonicalID, err := utils.CanonicalKeyID(k); err == nil {
			check = append(check, canonicalID)
		}
		for _, id := range check {
			p, _, err := tr.cryptoService.GetPrivateKey(id)
			if err == nil && p != nil {
				return nil
			}
		}
	}
	return signed.ErrNoKeys{KeyIDs: role.ListKeyIDs()}
}
Example #5
0
// adds a TUF Change template to the given roles
func addChange(cl *changelist.FileChangelist, c changelist.Change, roles ...string) error {

	if len(roles) == 0 {
		roles = []string{data.CanonicalTargetsRole}
	}

	var changes []changelist.Change
	for _, role := range roles {
		role = strings.ToLower(role)

		// Ensure we can only add targets to the CanonicalTargetsRole,
		// or a Delegation role (which is <CanonicalTargetsRole>/something else)
		if role != data.CanonicalTargetsRole && !data.IsDelegation(role) {
			return data.ErrInvalidRole{
				Role:   role,
				Reason: "cannot add targets to this role",
			}
		}

		changes = append(changes, changelist.NewTufChange(
			c.Action(),
			role,
			c.Type(),
			c.Path(),
			c.Content(),
		))
	}

	for _, c := range changes {
		if err := cl.Add(c); err != nil {
			return err
		}
	}
	return nil
}
Example #6
0
// AddDelegation creates a new changelist entry to add a delegation to the repository
// when the changelist gets applied at publish time.  This does not do any validation
// other than checking the name of the delegation to add - all that will happen
// at publish time.
func (r *NotaryRepository) AddDelegation(name string, threshold int,
	delegationKeys []data.PublicKey, paths []string) error {

	if !data.IsDelegation(name) {
		return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
	}

	cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist"))
	if err != nil {
		return err
	}
	defer cl.Close()

	logrus.Debugf(`Adding delegation "%s" with threshold %d, and %d keys\n`,
		name, threshold, len(delegationKeys))

	tdJSON, err := json.Marshal(&changelist.TufDelegation{
		NewThreshold: threshold,
		AddKeys:      data.KeyList(delegationKeys),
		AddPaths:     paths,
	})
	if err != nil {
		return err
	}

	template := changelist.NewTufChange(
		changelist.ActionCreate,
		name,
		changelist.TypeTargetsDelegation,
		"", // no path
		tdJSON,
	)

	return addChange(cl, template, name)
}
Example #7
0
func applyChangelist(repo *tuf.Repo, cl changelist.Changelist) error {
	it, err := cl.NewIterator()
	if err != nil {
		return err
	}
	index := 0
	for it.HasNext() {
		c, err := it.Next()
		if err != nil {
			return err
		}
		isDel := data.IsDelegation(c.Scope())
		switch {
		case c.Scope() == changelist.ScopeTargets || isDel:
			err = applyTargetsChange(repo, c)
		case c.Scope() == changelist.ScopeRoot:
			err = applyRootChange(repo, c)
		default:
			logrus.Debug("scope not supported: ", c.Scope())
		}
		index++
		if err != nil {
			return err
		}
	}
	logrus.Debugf("applied %d change(s)", index)
	return nil
}
Example #8
0
// GetDelegation finds the role entry representing the provided
// role name along with its associated public keys, or ErrInvalidRole
func (tr *Repo) GetDelegation(role string) (*data.Role, data.Keys, error) {
	if !data.IsDelegation(role) {
		return nil, nil, data.ErrInvalidRole{Role: role, Reason: "not a valid delegated role"}
	}

	parent := path.Dir(role)

	// check the parent role
	if _, err := tr.GetDelegationRole(parent); parent != data.CanonicalTargetsRole && err != nil {
		return nil, nil, data.ErrInvalidRole{Role: role, Reason: "parent role not found"}
	}

	// check the parent role's metadata
	p, ok := tr.Targets[parent]
	if !ok { // the parent targetfile may not exist yet, so it can't be in the list
		return nil, nil, data.ErrNoSuchRole{Role: role}
	}

	foundAt := utils.FindRoleIndex(p.Signed.Delegations.Roles, role)
	if foundAt < 0 {
		return nil, nil, data.ErrNoSuchRole{Role: role}
	}
	delegationRole := p.Signed.Delegations.Roles[foundAt]
	keys := make(data.Keys)
	for _, keyID := range delegationRole.KeyIDs {
		keys[keyID] = p.Signed.Delegations.Keys[keyID]
	}
	return delegationRole, keys, nil
}
Example #9
0
// UpdateDelegationPaths updates the appropriate delegation's paths.
// It is not allowed to create a new delegation.
func (tr *Repo) UpdateDelegationPaths(roleName string, addPaths, removePaths []string, clearPaths bool) error {
	if !data.IsDelegation(roleName) {
		return data.ErrInvalidRole{Role: roleName, Reason: "not a valid delegated role"}
	}
	parent := path.Dir(roleName)

	if err := tr.VerifyCanSign(parent); err != nil {
		return err
	}

	// check the parent role's metadata
	_, ok := tr.Targets[parent]
	if !ok { // the parent targetfile may not exist yet
		// if not, this is an error because a delegation must exist to edit only paths
		return data.ErrInvalidRole{Role: roleName, Reason: "no valid delegated role exists"}
	}

	// Walk to the parent of this delegation, since that is where its role metadata exists
	// We do not have to verify that the walker reached its desired role in this scenario
	// since we've already done another walk to the parent role in VerifyCanSign
	err := tr.WalkTargets("", parent, delegationUpdateVisitor(roleName, data.KeyList{}, []string{}, addPaths, removePaths, clearPaths, notary.MinThreshold))
	if err != nil {
		return err
	}
	return nil
}
Example #10
0
func applyChangelist(repo *tuf.Repo, invalid *tuf.Repo, cl changelist.Changelist) error {
	it, err := cl.NewIterator()
	if err != nil {
		return err
	}
	index := 0
	for it.HasNext() {
		c, err := it.Next()
		if err != nil {
			return err
		}
		isDel := data.IsDelegation(c.Scope()) || data.IsWildDelegation(c.Scope())
		switch {
		case c.Scope() == changelist.ScopeTargets || isDel:
			err = applyTargetsChange(repo, invalid, c)
		case c.Scope() == changelist.ScopeRoot:
			err = applyRootChange(repo, c)
		default:
			return fmt.Errorf("scope not supported: %s", c.Scope())
		}
		if err != nil {
			logrus.Debugf("error attempting to apply change #%d: %s, on scope: %s path: %s type: %s", index, c.Action(), c.Scope(), c.Path(), c.Type())
			return err
		}
		index++
	}
	logrus.Debugf("applied %d change(s)", index)
	return nil
}
Example #11
0
// RotateKey rotates the key for a role - this can invalidate that role's metadata
// if it is not signed by that key.  Particularly if the key being rotated is the
// root key, because it is not signed by the new key, only the old key.
func (m *MetadataSwizzler) RotateKey(role string, key data.PublicKey) error {
	roleSpecifier := data.CanonicalRootRole
	if data.IsDelegation(role) {
		roleSpecifier = path.Dir(role)
	}

	b, err := m.MetadataCache.GetSized(roleSpecifier, store.NoSizeLimit)
	if err != nil {
		return err
	}

	signedThing := &data.Signed{}
	if err := json.Unmarshal(b, signedThing); err != nil {
		return err
	}

	// get keys before the keys are rotated
	pubKeys, err := getPubKeys(m.CryptoService, signedThing, roleSpecifier)
	if err != nil {
		return err
	}

	if roleSpecifier == data.CanonicalRootRole {
		signedRoot, err := data.RootFromSigned(signedThing)
		if err != nil {
			return err
		}
		signedRoot.Signed.Roles[role].KeyIDs = []string{key.ID()}
		signedRoot.Signed.Keys[key.ID()] = key
		if signedThing, err = signedRoot.ToSigned(); err != nil {
			return err
		}
	} else {
		signedTargets, err := data.TargetsFromSigned(signedThing, roleSpecifier)
		if err != nil {
			return err
		}
		for _, roleObject := range signedTargets.Signed.Delegations.Roles {
			if roleObject.Name == role {
				roleObject.KeyIDs = []string{key.ID()}
				break
			}
		}
		signedTargets.Signed.Delegations.Keys[key.ID()] = key
		if signedThing, err = signedTargets.ToSigned(); err != nil {
			return err
		}
	}

	metaBytes, err := serializeMetadata(m.CryptoService, signedThing, roleSpecifier, pubKeys...)
	if err != nil {
		return err
	}
	return m.MetadataCache.Set(roleSpecifier, metaBytes)
}
Example #12
0
// InitTargets initializes an empty targets, and returns the new empty target
func (tr *Repo) InitTargets(role string) (*data.SignedTargets, error) {
	if !data.IsDelegation(role) && role != data.CanonicalTargetsRole {
		return nil, data.ErrInvalidRole{
			Role:   role,
			Reason: fmt.Sprintf("role is not a valid targets role name: %s", role),
		}
	}
	targets := data.NewTargets()
	tr.Targets[role] = targets
	return targets, nil
}
Example #13
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 !data.IsDelegation(role.Name) {
		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
	}

	// 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).

	utils.RemoveUnusedKeys(p)

	return nil
}
Example #14
0
// RotateKey removes all existing keys associated with the role, and either
// creates and adds one new key or delegates managing the key to the server.
// These changes are staged in a changelist until publish is called.
func (r *NotaryRepository) RotateKey(role string, serverManagesKey bool) error {
	// We currently support remotely managing timestamp and snapshot keys
	canBeRemoteKey := role == data.CanonicalTimestampRole || role == data.CanonicalSnapshotRole
	// And locally managing root, targets, and snapshot keys
	canBeLocalKey := (role == data.CanonicalSnapshotRole || role == data.CanonicalTargetsRole ||
		role == data.CanonicalRootRole)

	switch {
	case !data.ValidRole(role) || data.IsDelegation(role):
		return fmt.Errorf("notary does not currently permit rotating the %s key", role)
	case serverManagesKey && !canBeRemoteKey:
		return ErrInvalidRemoteRole{Role: role}
	case !serverManagesKey && !canBeLocalKey:
		return ErrInvalidLocalRole{Role: role}
	}

	var (
		pubKey    data.PublicKey
		err       error
		errFmtMsg string
	)
	switch serverManagesKey {
	case true:
		pubKey, err = getRemoteKey(r.baseURL, r.gun, role, r.roundTrip)
		errFmtMsg = "unable to rotate remote key: %s"
	default:
		pubKey, err = r.CryptoService.Create(role, r.gun, data.ECDSAKey)
		errFmtMsg = "unable to generate key: %s"
	}

	if err != nil {
		return fmt.Errorf(errFmtMsg, err)
	}

	// if this is a root role, generate a root cert for the public key
	if role == data.CanonicalRootRole {
		privKey, _, err := r.CryptoService.GetPrivateKey(pubKey.ID())
		if err != nil {
			return err
		}
		pubKey, err = rootCertKey(r.gun, privKey)
		if err != nil {
			return err
		}
	}

	cl := changelist.NewMemChangelist()
	if err := r.rootFileKeyChange(cl, role, changelist.ActionCreate, pubKey); err != nil {
		return err
	}
	return r.publish(cl)
}
Example #15
0
// SetThreshold sets a threshold for a metadata role - can invalidate metadata for which
// the threshold is increased, if there aren't enough signatures or can be invalid because
// the threshold is 0
func (m *MetadataSwizzler) SetThreshold(role string, newThreshold int) error {
	roleSpecifier := data.CanonicalRootRole
	if data.IsDelegation(role) {
		roleSpecifier = path.Dir(role)
	}

	b, err := m.MetadataCache.GetSized(roleSpecifier, store.NoSizeLimit)
	if err != nil {
		return err
	}

	signedThing := &data.Signed{}
	if err := json.Unmarshal(b, signedThing); err != nil {
		return err
	}

	if roleSpecifier == data.CanonicalRootRole {
		signedRoot, err := data.RootFromSigned(signedThing)
		if err != nil {
			return err
		}
		signedRoot.Signed.Roles[role].Threshold = newThreshold
		if signedThing, err = signedRoot.ToSigned(); err != nil {
			return err
		}
	} else {
		signedTargets, err := data.TargetsFromSigned(signedThing, roleSpecifier)
		if err != nil {
			return err
		}
		for _, roleObject := range signedTargets.Signed.Delegations.Roles {
			if roleObject.Name == role {
				roleObject.Threshold = newThreshold
				break
			}
		}
		if signedThing, err = signedTargets.ToSigned(); err != nil {
			return err
		}
	}

	var metaBytes []byte
	pubKeys, err := getPubKeys(m.CryptoService, signedThing, roleSpecifier)
	if err == nil {
		metaBytes, err = serializeMetadata(m.CryptoService, signedThing, roleSpecifier, pubKeys...)
	}

	if err != nil {
		return err
	}
	return m.MetadataCache.Set(roleSpecifier, metaBytes)
}
Example #16
0
// applies a function repeatedly, falling back on the parent role, until it no
// longer can
func doWithRoleFallback(role string, doFunc func(string) error) error {
	for role == data.CanonicalTargetsRole || data.IsDelegation(role) {
		err := doFunc(role)
		if err == nil {
			return nil
		}
		if _, ok := err.(data.ErrInvalidRole); !ok {
			return err
		}
		role = path.Dir(role)
	}
	return data.ErrInvalidRole{Role: role}
}
Example #17
0
// AddKey stores the contents of a PEM-encoded private key as a PEM block
func (s *KeyMemoryStore) AddKey(keyInfo KeyInfo, privKey data.PrivateKey) error {
	s.Lock()
	defer s.Unlock()
	if keyInfo.Role == data.CanonicalRootRole || data.IsDelegation(keyInfo.Role) || !data.ValidRole(keyInfo.Role) {
		keyInfo.Gun = ""
	}
	err := addKey(s, s.PassRetriever, s.cachedKeys, filepath.Join(keyInfo.Gun, privKey.ID()), keyInfo.Role, privKey)
	if err != nil {
		return err
	}
	s.keyInfoMap[privKey.ID()] = keyInfo
	return nil
}
Example #18
0
// GetDelegationRole gets a delegation role from this repo's metadata, walking from the targets role down to the delegation itself
func (tr *Repo) GetDelegationRole(name string) (data.DelegationRole, error) {
	if !data.IsDelegation(name) {
		return data.DelegationRole{}, data.ErrInvalidRole{Role: name, Reason: "invalid delegation name"}
	}
	if tr.Root == nil {
		return data.DelegationRole{}, ErrNotLoaded{data.CanonicalRootRole}
	}
	_, ok := tr.Root.Signed.Roles[data.CanonicalTargetsRole]
	if !ok {
		return data.DelegationRole{}, ErrNotLoaded{data.CanonicalTargetsRole}
	}
	// Traverse target metadata, down to delegation itself
	// Get all public keys for the base role from TUF metadata
	_, ok = tr.Targets[data.CanonicalTargetsRole]
	if !ok {
		return data.DelegationRole{}, ErrNotLoaded{data.CanonicalTargetsRole}
	}

	// Start with top level roles in targets. Walk the chain of ancestors
	// until finding the desired role, or we run out of targets files to search.
	var foundRole *data.DelegationRole
	buildDelegationRoleVisitor := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
		// Try to find the delegation and build a DelegationRole structure
		for _, role := range tgt.Signed.Delegations.Roles {
			if role.Name == name {
				delgRole, err := tgt.BuildDelegationRole(name)
				if err != nil {
					return err
				}
				foundRole = &delgRole
				return StopWalk{}
			}
		}
		return nil
	}

	// Walk to the parent of this delegation, since that is where its role metadata exists
	err := tr.WalkTargets("", path.Dir(name), buildDelegationRoleVisitor)
	if err != nil {
		return data.DelegationRole{}, err
	}

	// We never found the delegation. In the context of this repo it is considered
	// invalid. N.B. it may be that it existed at one point but an ancestor has since
	// been modified/removed.
	if foundRole == nil {
		return data.DelegationRole{}, data.ErrInvalidRole{Role: name, Reason: "delegation does not exist"}
	}

	return *foundRole, nil
}
Example #19
0
func loadAndValidateTargets(gun string, repo *tuf.Repo, roles map[string]storage.MetaUpdate, store storage.MetaStore) ([]storage.MetaUpdate, error) {
	targetsRoles := make(utils.RoleList, 0)
	for role := range roles {
		if role == data.CanonicalTargetsRole || data.IsDelegation(role) {
			targetsRoles = append(targetsRoles, role)
		}
	}

	// N.B. RoleList sorts paths with fewer segments first.
	// By sorting, we'll always process shallower targets updates before deeper
	// ones (i.e. we'll load and validate targets before targets/foo). This
	// helps ensure we only load from storage when necessary in a cleaner way.
	sort.Sort(targetsRoles)

	updatesToApply := make([]storage.MetaUpdate, 0, len(targetsRoles))
	for _, role := range targetsRoles {
		// don't load parent if current role is "targets",
		// we must load all ancestor roles for delegations to validate the full parent chain
		ancestorRole := role
		for ancestorRole != data.CanonicalTargetsRole {
			ancestorRole = path.Dir(ancestorRole)
			if _, ok := repo.Targets[ancestorRole]; !ok {
				err := loadTargetsFromStore(gun, ancestorRole, repo, store)
				if err != nil {
					return nil, err
				}
			}
		}
		var (
			t   *data.SignedTargets
			err error
		)
		if t, err = validateTargets(role, roles, repo); err != nil {
			if _, ok := err.(data.ErrInvalidRole); ok {
				// role wasn't found in its parent. It has been removed
				// or never existed. Drop this role from the update
				// (by not adding it to updatesToApply)
				continue
			}
			logrus.Error("ErrBadTargets: ", err.Error())
			return nil, validation.ErrBadTargets{Msg: err.Error()}
		}
		// this will load keys and roles into the kdb
		err = repo.SetTargets(role, t)
		if err != nil {
			return nil, err
		}
		updatesToApply = append(updatesToApply, roles[role])
	}
	return updatesToApply, nil
}
Example #20
0
func loadAndValidateTargets(gun string, builder tuf.RepoBuilder, roles map[string]storage.MetaUpdate, store storage.MetaStore) ([]storage.MetaUpdate, error) {
	targetsRoles := make(utils.RoleList, 0)
	for role := range roles {
		if role == data.CanonicalTargetsRole || data.IsDelegation(role) {
			targetsRoles = append(targetsRoles, role)
		}
	}

	// N.B. RoleList sorts paths with fewer segments first.
	// By sorting, we'll always process shallower targets updates before deeper
	// ones (i.e. we'll load and validate targets before targets/foo). This
	// helps ensure we only load from storage when necessary in a cleaner way.
	sort.Sort(targetsRoles)

	updatesToApply := make([]storage.MetaUpdate, 0, len(targetsRoles))
	for _, roleName := range targetsRoles {
		// don't load parent if current role is "targets",
		// we must load all ancestor roles, starting from `targets` and working down,
		// for delegations to validate the full parent chain
		var parentsToLoad []string
		ancestorRole := roleName
		for ancestorRole != data.CanonicalTargetsRole {
			ancestorRole = path.Dir(ancestorRole)
			if !builder.IsLoaded(ancestorRole) {
				parentsToLoad = append(parentsToLoad, ancestorRole)
			}
		}
		for i := len(parentsToLoad) - 1; i >= 0; i-- {
			if err := loadFromStore(gun, parentsToLoad[i], builder, store); err != nil {
				// if the parent doesn't exist, just keep going - loading the role will eventually fail
				// due to it being an invalid role
				if _, ok := err.(storage.ErrNotFound); !ok {
					return nil, err
				}
			}
		}

		if err := builder.Load(roleName, roles[roleName].Data, 1, false); err != nil {
			logrus.Error("ErrBadTargets: ", err.Error())
			return nil, validation.ErrBadTargets{Msg: err.Error()}
		}
		updatesToApply = append(updatesToApply, roles[roleName])
	}

	return updatesToApply, nil
}
Example #21
0
func checkRotationInput(role string, serverManaged bool) error {
	// We currently support remotely managing timestamp and snapshot keys
	canBeRemoteKey := role == data.CanonicalTimestampRole || role == data.CanonicalSnapshotRole
	// And locally managing root, targets, and snapshot keys
	canBeLocalKey := role == data.CanonicalSnapshotRole || role == data.CanonicalTargetsRole ||
		role == data.CanonicalRootRole

	switch {
	case !data.ValidRole(role) || data.IsDelegation(role):
		return fmt.Errorf("notary does not currently permit rotating the %s key", role)
	case serverManaged && !canBeRemoteKey:
		return ErrInvalidRemoteRole{Role: role}
	case !serverManaged && !canBeLocalKey:
		return ErrInvalidLocalRole{Role: role}
	}
	return nil
}
Example #22
0
// RemoveDelegationRole creates a changelist to remove all paths and keys from a role, and delete the role in its entirety.
func (r *NotaryRepository) RemoveDelegationRole(name string) error {

	if !data.IsDelegation(name) {
		return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
	}

	cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist"))
	if err != nil {
		return err
	}
	defer cl.Close()

	logrus.Debugf(`Removing delegation "%s"\n`, name)

	template := newDeleteDelegationChange(name, nil)
	return addChange(cl, template, name)
}
Example #23
0
// RemoveDelegation creates a new changelist entry to remove a delegation from
// the repository when the changelist gets applied at publish time.
// This does not validate that the delegation exists, since one might exist
// after applying all changes.
func (r *NotaryRepository) RemoveDelegation(name string, keyIDs, paths []string, removeAll bool) error {

	if !data.IsDelegation(name) {
		return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
	}

	cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist"))
	if err != nil {
		return err
	}
	defer cl.Close()

	logrus.Debugf(`Removing delegation "%s"\n`, name)
	var template *changelist.TufChange

	// We use the Delete action only for force removal, Update is used for removing individual keys and paths
	if removeAll {
		template = changelist.NewTufChange(
			changelist.ActionDelete,
			name,
			changelist.TypeTargetsDelegation,
			"",  // no path
			nil, // deleting role, no data needed
		)

	} else {
		tdJSON, err := json.Marshal(&changelist.TufDelegation{
			RemoveKeys:  keyIDs,
			RemovePaths: paths,
		})
		if err != nil {
			return err
		}

		template = changelist.NewTufChange(
			changelist.ActionUpdate,
			name,
			changelist.TypeTargetsDelegation,
			"", // no path
			tdJSON,
		)
	}

	return addChange(cl, template, name)
}
Example #24
0
// DeleteDelegation removes a delegated targets role from its parent
// targets object. It also deletes the delegation from the snapshot.
// DeleteDelegation will only make use of the role Name field.
func (tr *Repo) DeleteDelegation(role data.Role) error {
	if !data.IsDelegation(role.Name) {
		return data.ErrInvalidRole{Role: role.Name, Reason: "not a valid delegated role"}
	}
	// the role variable must not be used past this assignment for safety
	name := role.Name

	parent := path.Dir(name)
	if err := tr.VerifyCanSign(parent); err != nil {
		return err
	}

	// delete delegated data from Targets map and Snapshot - if they don't
	// exist, these are no-op
	delete(tr.Targets, name)
	tr.Snapshot.DeleteMeta(name)

	p, ok := tr.Targets[parent]
	if !ok {
		// if there is no parent metadata (the role exists though), then this
		// is as good as done.
		return nil
	}

	foundAt := utils.FindRoleIndex(p.Signed.Delegations.Roles, name)

	if foundAt >= 0 {
		var roles []*data.Role
		// slice out deleted role
		roles = append(roles, p.Signed.Delegations.Roles[:foundAt]...)
		if foundAt+1 < len(p.Signed.Delegations.Roles) {
			roles = append(roles, p.Signed.Delegations.Roles[foundAt+1:]...)
		}
		p.Signed.Delegations.Roles = roles

		utils.RemoveUnusedKeys(p)

		p.Dirty = true
	} // if the role wasn't found, it's a good as deleted

	return nil
}
Example #25
0
// AddKey stores the contents of a PEM-encoded private key as a PEM block
func (s *GenericKeyStore) AddKey(keyInfo KeyInfo, privKey data.PrivateKey) error {
	var (
		chosenPassphrase string
		giveup           bool
		err              error
		pemPrivKey       []byte
	)
	s.Lock()
	defer s.Unlock()
	if keyInfo.Role == data.CanonicalRootRole || data.IsDelegation(keyInfo.Role) || !data.ValidRole(keyInfo.Role) {
		keyInfo.Gun = ""
	}
	keyID := privKey.ID()
	for attempts := 0; ; attempts++ {
		chosenPassphrase, giveup, err = s.PassRetriever(keyID, keyInfo.Role, true, attempts)
		if err == nil {
			break
		}
		if giveup || attempts > 10 {
			return ErrAttemptsExceeded{}
		}
	}

	if chosenPassphrase != "" {
		pemPrivKey, err = utils.EncryptPrivateKey(privKey, keyInfo.Role, keyInfo.Gun, chosenPassphrase)
	} else {
		pemPrivKey, err = utils.KeyToPEM(privKey, keyInfo.Role, keyInfo.Gun)
	}

	if err != nil {
		return err
	}

	s.cachedKeys[keyID] = &cachedKey{alias: keyInfo.Role, key: privKey}
	err = s.store.Set(keyID, pemPrivKey)
	if err != nil {
		return err
	}
	s.keyInfoMap[privKey.ID()] = keyInfo
	return nil
}
Example #26
0
// ListRoles returns a list of RoleWithSignatures objects for this repo
// This represents the latest metadata for each role in this repo
func (r *NotaryRepository) ListRoles() ([]RoleWithSignatures, error) {
	// Update to latest repo state
	_, err := r.Update(false)
	if err != nil {
		return nil, err
	}

	// Get all role info from our updated keysDB, can be empty
	roles := r.tufRepo.GetAllLoadedRoles()

	var roleWithSigs []RoleWithSignatures

	// Populate RoleWithSignatures with Role from keysDB and signatures from TUF metadata
	for _, role := range roles {
		roleWithSig := RoleWithSignatures{Role: *role, Signatures: nil}
		switch role.Name {
		case data.CanonicalRootRole:
			roleWithSig.Signatures = r.tufRepo.Root.Signatures
		case data.CanonicalTargetsRole:
			roleWithSig.Signatures = r.tufRepo.Targets[data.CanonicalTargetsRole].Signatures
		case data.CanonicalSnapshotRole:
			roleWithSig.Signatures = r.tufRepo.Snapshot.Signatures
		case data.CanonicalTimestampRole:
			roleWithSig.Signatures = r.tufRepo.Timestamp.Signatures
		default:
			// If the role isn't a delegation, we should error -- this is only possible if we have invalid state
			if !data.IsDelegation(role.Name) {
				return nil, data.ErrInvalidRole{Role: role.Name, Reason: "invalid role name"}
			}
			if _, ok := r.tufRepo.Targets[role.Name]; ok {
				// We'll only find a signature if we've published any targets with this delegation
				roleWithSig.Signatures = r.tufRepo.Targets[role.Name].Signatures
			}
		}
		roleWithSigs = append(roleWithSigs, roleWithSig)
	}
	return roleWithSigs, nil
}
Example #27
0
func testAddKeyWithRole(t *testing.T, role, expectedSubdir string) {
	gun := "docker.com/notary"
	testExt := "key"

	// Temporary directory where test files will be created
	tempBaseDir, err := ioutil.TempDir("", "notary-test-")
	require.NoError(t, err, "failed to create a temporary directory")
	defer os.RemoveAll(tempBaseDir)
	// Create our store
	store, err := NewKeyFileStore(tempBaseDir, passphraseRetriever)
	require.NoError(t, err, "failed to create new key filestore")

	privKey, err := GenerateECDSAKey(rand.Reader)
	require.NoError(t, err, "could not generate private key")

	// Since we're generating this manually we need to add the extension '.'
	expectedFilePath := filepath.Join(tempBaseDir, notary.PrivDir, expectedSubdir, privKey.ID()+"."+testExt)

	// Call the AddKey function
	err = store.AddKey(KeyInfo{Role: role, Gun: gun}, privKey)
	require.NoError(t, err, "failed to add key to store")

	// Check to see if file exists
	b, err := ioutil.ReadFile(expectedFilePath)
	require.NoError(t, err, "expected file not found")
	require.Contains(t, string(b), "-----BEGIN EC PRIVATE KEY-----")

	// Check that we have the role and gun info for this key's ID
	keyInfo, ok := store.keyInfoMap[privKey.ID()]
	require.True(t, ok)
	require.Equal(t, role, keyInfo.Role)
	if role == data.CanonicalRootRole || data.IsDelegation(role) || !data.ValidRole(role) {
		require.Empty(t, keyInfo.Gun)
	} else {
		require.Equal(t, gun, keyInfo.Gun)
	}
}
Example #28
0
// ClearDelegationPaths creates a changelist entry to remove all paths from an existing delegation.
func (r *NotaryRepository) ClearDelegationPaths(name string) error {

	if !data.IsDelegation(name) {
		return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
	}

	cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist"))
	if err != nil {
		return err
	}
	defer cl.Close()

	logrus.Debugf(`Removing all paths from delegation "%s"\n`, name)

	tdJSON, err := json.Marshal(&changelist.TufDelegation{
		ClearAllPaths: true,
	})
	if err != nil {
		return err
	}

	template := newUpdateDelegationChange(name, tdJSON)
	return addChange(cl, template, name)
}
Example #29
0
// AddDelegationPaths creates a changelist entry to add provided paths to an existing delegation.
// This method cannot create a new delegation itself because the role must meet the key threshold upon creation.
func (r *NotaryRepository) AddDelegationPaths(name string, paths []string) error {

	if !data.IsDelegation(name) {
		return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
	}

	cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist"))
	if err != nil {
		return err
	}
	defer cl.Close()

	logrus.Debugf(`Adding %s paths to delegation %s\n`, paths, name)

	tdJSON, err := json.Marshal(&changelist.TufDelegation{
		AddPaths: paths,
	})
	if err != nil {
		return err
	}

	template := newCreateDelegationChange(name, tdJSON)
	return addChange(cl, template, name)
}
Example #30
0
// RemoveDelegationKeys creates a changelist entry to remove provided keys from an existing delegation.
// When this changelist is applied, if the specified keys are the only keys left in the role,
// the role itself will be deleted in its entirety.
// It can also delete a key from all delegations under a parent using a name
// with a wildcard at the end.
func (r *NotaryRepository) RemoveDelegationKeys(name string, keyIDs []string) error {

	if !data.IsDelegation(name) && !data.IsWildDelegation(name) {
		return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
	}

	cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist"))
	if err != nil {
		return err
	}
	defer cl.Close()

	logrus.Debugf(`Removing %s keys from delegation "%s"\n`, keyIDs, name)

	tdJSON, err := json.Marshal(&changelist.TUFDelegation{
		RemoveKeys: keyIDs,
	})
	if err != nil {
		return err
	}

	template := newUpdateDelegationChange(name, tdJSON)
	return addChange(cl, template, name)
}