Merge "vdl: Remove rpc.BindOpt from generated code."
diff --git a/cmd/vrun/vrun.go b/cmd/vrun/vrun.go
index e604d9f..4bdafd1 100644
--- a/cmd/vrun/vrun.go
+++ b/cmd/vrun/vrun.go
@@ -6,6 +6,7 @@
 
 import (
 	"os"
+	"os/exec"
 	"path/filepath"
 	"syscall"
 	"time"
@@ -126,7 +127,12 @@
 		}
 		conn.Close()
 	}
-	err := syscall.Exec(cmd[0], cmd, os.Environ())
+	p, err := exec.LookPath(cmd[0])
+	if err != nil {
+		vlog.Errorf("Couldn't find %q", cmd[0])
+		return err
+	}
+	err = syscall.Exec(p, cmd, os.Environ())
 	vlog.Errorf("Couldn't exec %s.", cmd[0])
 	return err
 }
diff --git a/services/mgmt/lib/acls/hierarchical_authorizer.go b/services/mgmt/lib/acls/hierarchical_authorizer.go
index 142b59d..5b5e921 100644
--- a/services/mgmt/lib/acls/hierarchical_authorizer.go
+++ b/services/mgmt/lib/acls/hierarchical_authorizer.go
@@ -9,13 +9,15 @@
 	"v.io/v23/security"
 	"v.io/v23/security/access"
 	"v.io/x/lib/vlog"
+
+	"v.io/x/ref/profiles/internal/rpc"
 )
 
-// hierarchicalAuthorizer manages a pair of authorizers for two-level
-// inheritance of AccessLists.
+// hierarchicalAuthorizer contains the state needed to implement
+// hierarchical authorization in the Authorize method.
 type hierarchicalAuthorizer struct {
-	child          security.Authorizer
-	rootAccessList access.AccessList
+	rootDir, childDir string
+	get               TAMGetter
 }
 
 // TAMGetter defines an abstract interface that a customer of
@@ -38,48 +40,55 @@
 	return rootAuth, nil
 }
 
-// NewHierarchicalAuthorizer creates a new hierarchicalAuthorizer
+// NewHierarchicalAuthorizer creates a new hierarchicalAuthorizer: one
+// that implements a "root" like concept: admin rights at the root of
+// a server can invoke RPCs regardless of permissions set on child objects.
 func NewHierarchicalAuthorizer(rootDir, childDir string, get TAMGetter) (security.Authorizer, error) {
-	rootTam, intentionallyEmpty, err := get.TAMForPath(rootDir)
-	if err != nil {
-		return nil, err
-	} else if intentionallyEmpty {
-		vlog.VI(2).Infof("TAMForPath(%s) is intentionally empty", rootDir)
-		return nil, nil
-	}
-
-	// We are at the root so exit early.
-	if rootDir == childDir {
-		return mkRootAuth(rootTam)
-	}
-
-	// This is not fatal: the childDir may not exist if we are invoking
-	// a Create() method so we only use the root AccessList.
-	childTam, intentionallyEmpty, err := get.TAMForPath(childDir)
-	if err != nil {
-		return nil, err
-	} else if intentionallyEmpty {
-		return mkRootAuth(rootTam)
-	}
-
-	childAuth, err := access.PermissionsAuthorizer(childTam, access.TypicalTagType())
-	if err != nil {
-		vlog.Errorf("Successfully obtained an AccessList from the filesystem but PermissionsAuthorizer couldn't use it: %v", err)
-		return nil, err
-	}
-
 	return &hierarchicalAuthorizer{
-		child:          childAuth,
-		rootAccessList: rootTam[string(access.Admin)],
+		rootDir:  rootDir,
+		childDir: childDir,
+		get:      get,
 	}, nil
 }
 
-// Authorize provides two-levels of authorization. Admin permission
-// on the root provides a "superuser"-like power for administering the
-// server using an instance of hierarchicalAuthorizer. Otherwise, the
-// default permissions of the named path apply.
 func (ha *hierarchicalAuthorizer) Authorize(ctx *context.T) error {
-	childErr := ha.child.Authorize(ctx)
+	rootPerms, intentionallyEmpty, err := ha.get.TAMForPath(ha.rootDir)
+	if err != nil {
+		return err
+	} else if intentionallyEmpty {
+		vlog.VI(2).Infof("TAMForPath(%s) is intentionally empty", ha.rootDir)
+		return defaultAuthorizer(ctx)
+	}
+
+	// We are at the root so exit early.
+	if ha.rootDir == ha.childDir {
+		a, err := mkRootAuth(rootPerms)
+		if err != nil {
+			return err
+		}
+		return a.Authorize(ctx)
+	}
+
+	// This is not fatal: the childDir may not exist if we are invoking
+	// a Create() method so we only use the root Permissions.
+	childPerms, intentionallyEmpty, err := ha.get.TAMForPath(ha.childDir)
+	if err != nil {
+		return err
+	} else if intentionallyEmpty {
+		a, err := mkRootAuth(rootPerms)
+		if err != nil {
+			return err
+		}
+		return a.Authorize(ctx)
+	}
+
+	childAuth, err := access.PermissionsAuthorizer(childPerms, access.TypicalTagType())
+	if err != nil {
+		vlog.Errorf("Successfully obtained a Permissions from the filesystem but PermissionsAuthorizer couldn't use it: %v", err)
+		return err
+	}
+
+	childErr := childAuth.Authorize(ctx)
 	if childErr == nil {
 		return nil
 	}
@@ -87,9 +96,35 @@
 	// Maybe the invoking principal can invoke this method because
 	// it has root permissions.
 	names, _ := security.RemoteBlessingNames(ctx)
-	if len(names) > 0 && ha.rootAccessList.Includes(names...) {
+	if len(names) > 0 && rootPerms[string(access.Admin)].Includes(names...) {
 		return nil
 	}
 
 	return childErr
 }
+
+// defaultAuthorizer implements an authorization policy that requires one end
+// of the RPC to have a blessing that makes it a delegate of the other.
+// TODO(rjkroege): Remove this when the defaultAuthorizer becomes public.
+func defaultAuthorizer(ctx *context.T) error {
+	var (
+		localNames             = security.LocalBlessingNames(ctx)
+		remoteNames, remoteErr = security.RemoteBlessingNames(ctx)
+	)
+	// Authorize if any element in localNames is a "delegate of" (i.e., has been
+	// blessed by) any element in remoteNames, OR vice-versa.
+	for _, l := range localNames {
+		if security.BlessingPattern(l).MatchedBy(remoteNames...) {
+			// l is a delegate of an element in remote.
+			return nil
+		}
+	}
+	for _, r := range remoteNames {
+		if security.BlessingPattern(r).MatchedBy(localNames...) {
+			// r is a delegate of an element in localNames.
+			return nil
+		}
+	}
+
+	return rpc.NewErrInvalidBlessings(nil, remoteNames, remoteErr, localNames)
+}