services/security/role: Add role server

This change adds the new role server and adds an option to vrun to run
commands using role blessings.

Each role has its own configuration file. The config file specifies a
list of members, i.e. who's allowed to use the role, in the form of a
set of blessing patterns. It also specifies 3 attributes that affect how
the role blessings are created:
  - Audit (bool): indicates that blessings will have a third-party
    caveat that will allow auditting of all uses.
  - Expiry (time.Duration string): the blessings will have an expiry
    caveat with expiry = now + value. An empty value removes the caveat.
  - Extend (bool): indicates that the blessing names will be extended
    with the full blessing name of the caller.

The blessing names that the role server returns look like:
  <server's blessing name> / <role> [ / <caller's blessing name> ]

There are two main use-cases for roles:
  - To use with access control, similar to a group membership. In this
    case, the service will use "root/roleserver/role" as blessing
    pattern, and the role config will have Extend=true.
  - To claim/bless a device or an application. Here, we need
    Extend=false, and no expiry (or a long one).

Note: The auditting / third-party caveat part is incomplete and will
require some changes in the security API to implement properly.

Change-Id: I1bc792c8c2e9e7522d8847580e7639a7f60f9f39
diff --git a/cmd/vrun/vrun.go b/cmd/vrun/vrun.go
index e115e98..820ace6 100644
--- a/cmd/vrun/vrun.go
+++ b/cmd/vrun/vrun.go
@@ -5,7 +5,6 @@
 package main
 
 import (
-	"flag"
 	"os"
 	"path/filepath"
 	"syscall"
@@ -15,6 +14,7 @@
 	"v.io/x/ref/lib/flags/consts"
 	"v.io/x/ref/security/agent"
 	"v.io/x/ref/security/agent/keymgr"
+	isecurity "v.io/x/ref/services/security"
 
 	"v.io/v23"
 	"v.io/v23/context"
@@ -24,8 +24,11 @@
 	_ "v.io/x/ref/profiles"
 )
 
-var durationFlag time.Duration
-var nameOverride string
+var (
+	durationFlag time.Duration
+	name         string
+	role         string
+)
 
 var cmdVrun = &cmdline.Command{
 	Run:      vrun,
@@ -39,10 +42,9 @@
 	syscall.CloseOnExec(3)
 	syscall.CloseOnExec(4)
 
-	flag.DurationVar(&durationFlag, "duration", 1*time.Hour, "Duration for the blessing.")
-	flag.StringVar(&nameOverride, "name", "", "Name to use for the blessing. Uses the command name if unset.")
 	cmdVrun.Flags.DurationVar(&durationFlag, "duration", 1*time.Hour, "Duration for the blessing.")
-	cmdVrun.Flags.StringVar(&nameOverride, "name", "", "Name to use for the blessing. Uses the command name if unset.")
+	cmdVrun.Flags.StringVar(&name, "name", "", "Name to use for the blessing. Uses the command name if unset.")
+	cmdVrun.Flags.StringVar(&role, "role", "", "Role object from which to request the blessing. If set, the blessings from this role server are used and --name is ignored. If not set, the default blessings of the calling principal are extended with --name.")
 
 	os.Exit(cmdVrun.Main())
 }
@@ -58,10 +60,30 @@
 	if err != nil {
 		return err
 	}
-	err = bless(ctx, principal, filepath.Base(args[0]))
-	if err != nil {
-		return err
+	if len(role) == 0 {
+		if len(name) == 0 {
+			name = filepath.Base(args[0])
+		}
+		if err := bless(ctx, principal, name); err != nil {
+			return err
+		}
+	} else {
+		// The role server expects the client's blessing name to end
+		// with RoleSuffix. This is to avoid accidentally granting role
+		// access to anything else that might have been blessed by the
+		// same principal.
+		if err := bless(ctx, principal, isecurity.RoleSuffix); err != nil {
+			return err
+		}
+		rCtx, err := v23.SetPrincipal(ctx, principal)
+		if err != nil {
+			return err
+		}
+		if err := setupRoleBlessings(rCtx, role); err != nil {
+			return err
+		}
 	}
+
 	return doExec(args, conn)
 }
 
@@ -71,9 +93,6 @@
 		vlog.Errorf("Couldn't create caveat")
 		return err
 	}
-	if 0 != len(nameOverride) {
-		name = nameOverride
-	}
 
 	rp := v23.GetPrincipal(ctx)
 	blessing, err := rp.Bless(p.PublicKey(), rp.BlessingStore().Default(), name, caveat)
@@ -139,3 +158,21 @@
 	}
 	return principal, conn, nil
 }
+
+func setupRoleBlessings(ctx *context.T, role string) error {
+	b, err := isecurity.RoleClient(role).SeekBlessings(ctx)
+	if err != nil {
+		return err
+	}
+	p := v23.GetPrincipal(ctx)
+	// TODO(rthellend,ashankar): Revisit this configuration.
+	// SetDefault: Should we expect users to want to act as a server on behalf of the role (by default?)
+	// AllPrincipals: Do we not want to be discriminating about which services we use the role blessing at.
+	if err := p.BlessingStore().SetDefault(b); err != nil {
+		return err
+	}
+	if _, err := p.BlessingStore().Set(b, security.AllPrincipals); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/services/security/discharger.vdl b/services/security/discharger.vdl
index d913b8d..040fe21 100644
--- a/services/security/discharger.vdl
+++ b/services/security/discharger.vdl
@@ -13,3 +13,8 @@
   // this caveat.
   Discharge(Caveat security.Caveat, Impetus security.DischargeImpetus) (Discharge security.WireDischarge | error)
 }
+
+error (
+	// Indicates that the Caveat does not require a discharge
+	NotAThirdPartyCaveat(c security.Caveat) { "en": "discharges are not required for non-third-party caveats (id: {c.id})" }
+)
diff --git a/services/security/discharger.vdl.go b/services/security/discharger.vdl.go
index 825acae..5645357 100644
--- a/services/security/discharger.vdl.go
+++ b/services/security/discharger.vdl.go
@@ -11,12 +11,28 @@
 	// VDL system imports
 	"v.io/v23"
 	"v.io/v23/context"
+	"v.io/v23/i18n"
 	"v.io/v23/rpc"
+	"v.io/v23/verror"
 
 	// VDL user imports
 	"v.io/v23/security"
 )
 
+var (
+	// Indicates that the Caveat does not require a discharge
+	ErrNotAThirdPartyCaveat = verror.Register("v.io/x/ref/services/security.NotAThirdPartyCaveat", verror.NoRetry, "{1:}{2:} discharges are not required for non-third-party caveats (id: {c.id})")
+)
+
+func init() {
+	i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrNotAThirdPartyCaveat.ID), "{1:}{2:} discharges are not required for non-third-party caveats (id: {c.id})")
+}
+
+// NewErrNotAThirdPartyCaveat returns an error with the ErrNotAThirdPartyCaveat ID.
+func NewErrNotAThirdPartyCaveat(ctx *context.T, c security.Caveat) error {
+	return verror.New(ErrNotAThirdPartyCaveat, ctx, c)
+}
+
 // DischargerClientMethods is the client interface
 // containing Discharger methods.
 //
diff --git a/services/security/discharger/discharger.go b/services/security/discharger/discharger.go
index e839380..30ad199 100644
--- a/services/security/discharger/discharger.go
+++ b/services/security/discharger/discharger.go
@@ -22,7 +22,7 @@
 	secCall := security.GetCall(ctx)
 	tp := caveat.ThirdPartyDetails()
 	if tp == nil {
-		return security.Discharge{}, fmt.Errorf("Caveat %v does not represent a third party caveat", caveat)
+		return security.Discharge{}, services.NewErrNotAThirdPartyCaveat(call.Context(), caveat)
 	}
 	if err := tp.Dischargeable(ctx); err != nil {
 		return security.Discharge{}, fmt.Errorf("third-party caveat %v cannot be discharged for this context: %v", tp, err)
diff --git a/services/security/role.vdl b/services/security/role.vdl
new file mode 100644
index 0000000..26e970a
--- /dev/null
+++ b/services/security/role.vdl
@@ -0,0 +1,24 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package security
+
+import "v.io/v23/security"
+
+// Role is an interface to request blessings from a role account server. The
+// returned blessings are bound to the client's public key thereby authorizing
+// the client to acquire the role. The server may tie the returned blessings
+// with the client's presented blessing name in order to maintain audit
+// information in the blessing.
+//
+// In order to avoid granting role blessings to all delegates of a principal,
+// the role server requires that each authorized blessing presented by the
+// client have the string "_role" as suffix.
+type Role interface {
+	SeekBlessings() (security.WireBlessings | error)
+}
+
+// Role.SeekBlessings will return an error if the requestor does not present
+// blessings that end in this suffix.
+const RoleSuffix = "_role"
diff --git a/services/security/role.vdl.go b/services/security/role.vdl.go
new file mode 100644
index 0000000..b90bb48
--- /dev/null
+++ b/services/security/role.vdl.go
@@ -0,0 +1,157 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file was auto-generated by the vanadium vdl tool.
+// Source: role.vdl
+
+package security
+
+import (
+	// VDL system imports
+	"v.io/v23"
+	"v.io/v23/context"
+	"v.io/v23/rpc"
+
+	// VDL user imports
+	"v.io/v23/security"
+)
+
+// Role.SeekBlessings will return an error if the requestor does not present
+// blessings that end in this suffix.
+const RoleSuffix = "_role"
+
+// RoleClientMethods is the client interface
+// containing Role methods.
+//
+// Role is an interface to request blessings from a role account server. The
+// returned blessings are bound to the client's public key thereby authorizing
+// the client to acquire the role. The server may tie the returned blessings
+// with the client's presented blessing name in order to maintain audit
+// information in the blessing.
+//
+// In order to avoid granting role blessings to all delegates of a principal,
+// the role server requires that each authorized blessing presented by the
+// client have the string "_role" as suffix.
+type RoleClientMethods interface {
+	SeekBlessings(*context.T, ...rpc.CallOpt) (security.Blessings, error)
+}
+
+// RoleClientStub adds universal methods to RoleClientMethods.
+type RoleClientStub interface {
+	RoleClientMethods
+	rpc.UniversalServiceMethods
+}
+
+// RoleClient returns a client stub for Role.
+func RoleClient(name string, opts ...rpc.BindOpt) RoleClientStub {
+	var client rpc.Client
+	for _, opt := range opts {
+		if clientOpt, ok := opt.(rpc.Client); ok {
+			client = clientOpt
+		}
+	}
+	return implRoleClientStub{name, client}
+}
+
+type implRoleClientStub struct {
+	name   string
+	client rpc.Client
+}
+
+func (c implRoleClientStub) c(ctx *context.T) rpc.Client {
+	if c.client != nil {
+		return c.client
+	}
+	return v23.GetClient(ctx)
+}
+
+func (c implRoleClientStub) SeekBlessings(ctx *context.T, opts ...rpc.CallOpt) (o0 security.Blessings, err error) {
+	var call rpc.ClientCall
+	if call, err = c.c(ctx).StartCall(ctx, c.name, "SeekBlessings", nil, opts...); err != nil {
+		return
+	}
+	err = call.Finish(&o0)
+	return
+}
+
+// RoleServerMethods is the interface a server writer
+// implements for Role.
+//
+// Role is an interface to request blessings from a role account server. The
+// returned blessings are bound to the client's public key thereby authorizing
+// the client to acquire the role. The server may tie the returned blessings
+// with the client's presented blessing name in order to maintain audit
+// information in the blessing.
+//
+// In order to avoid granting role blessings to all delegates of a principal,
+// the role server requires that each authorized blessing presented by the
+// client have the string "_role" as suffix.
+type RoleServerMethods interface {
+	SeekBlessings(rpc.ServerCall) (security.Blessings, error)
+}
+
+// RoleServerStubMethods is the server interface containing
+// Role methods, as expected by rpc.Server.
+// There is no difference between this interface and RoleServerMethods
+// since there are no streaming methods.
+type RoleServerStubMethods RoleServerMethods
+
+// RoleServerStub adds universal methods to RoleServerStubMethods.
+type RoleServerStub interface {
+	RoleServerStubMethods
+	// Describe the Role interfaces.
+	Describe__() []rpc.InterfaceDesc
+}
+
+// RoleServer returns a server stub for Role.
+// It converts an implementation of RoleServerMethods into
+// an object that may be used by rpc.Server.
+func RoleServer(impl RoleServerMethods) RoleServerStub {
+	stub := implRoleServerStub{
+		impl: impl,
+	}
+	// Initialize GlobState; always check the stub itself first, to handle the
+	// case where the user has the Glob method defined in their VDL source.
+	if gs := rpc.NewGlobState(stub); gs != nil {
+		stub.gs = gs
+	} else if gs := rpc.NewGlobState(impl); gs != nil {
+		stub.gs = gs
+	}
+	return stub
+}
+
+type implRoleServerStub struct {
+	impl RoleServerMethods
+	gs   *rpc.GlobState
+}
+
+func (s implRoleServerStub) SeekBlessings(call rpc.ServerCall) (security.Blessings, error) {
+	return s.impl.SeekBlessings(call)
+}
+
+func (s implRoleServerStub) Globber() *rpc.GlobState {
+	return s.gs
+}
+
+func (s implRoleServerStub) Describe__() []rpc.InterfaceDesc {
+	return []rpc.InterfaceDesc{RoleDesc}
+}
+
+// RoleDesc describes the Role interface.
+var RoleDesc rpc.InterfaceDesc = descRole
+
+// descRole hides the desc to keep godoc clean.
+var descRole = rpc.InterfaceDesc{
+	Name:    "Role",
+	PkgPath: "v.io/x/ref/services/security",
+	Doc:     "// Role is an interface to request blessings from a role account server. The\n// returned blessings are bound to the client's public key thereby authorizing\n// the client to acquire the role. The server may tie the returned blessings\n// with the client's presented blessing name in order to maintain audit\n// information in the blessing.\n//\n// In order to avoid granting role blessings to all delegates of a principal,\n// the role server requires that each authorized blessing presented by the\n// client have the string \"_role\" as suffix.",
+	Methods: []rpc.MethodDesc{
+		{
+			Name: "SeekBlessings",
+			OutArgs: []rpc.ArgDesc{
+				{"", ``}, // security.Blessings
+			},
+		},
+	},
+}
diff --git a/services/security/roled/internal/caveats.vdl b/services/security/roled/internal/caveats.vdl
new file mode 100644
index 0000000..0ad318c
--- /dev/null
+++ b/services/security/roled/internal/caveats.vdl
@@ -0,0 +1,18 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+	"v.io/v23/security"
+	"v.io/v23/uniqueid"
+)
+
+const (
+	// LoggingCaveat is a caveat that will always validate but it logs the parameter on every attempt to validate it.
+	LoggingCaveat = security.CaveatDescriptor{
+		Id:        uniqueid.Id{0xb0, 0x34, 0x1c, 0xed, 0xe2, 0xdf, 0x81, 0xbd, 0xed, 0x70, 0x97, 0xbb, 0x55, 0xad, 0x80, 0x0},
+		ParamType: typeobject([]string),
+	}
+)
diff --git a/services/security/roled/internal/caveats.vdl.go b/services/security/roled/internal/caveats.vdl.go
new file mode 100644
index 0000000..9a22ce7
--- /dev/null
+++ b/services/security/roled/internal/caveats.vdl.go
@@ -0,0 +1,40 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file was auto-generated by the vanadium vdl tool.
+// Source: caveats.vdl
+
+package internal
+
+import (
+	// VDL system imports
+	"v.io/v23/vdl"
+
+	// VDL user imports
+	"v.io/v23/security"
+	"v.io/v23/uniqueid"
+)
+
+// LoggingCaveat is a caveat that will always validate but it logs the parameter on every attempt to validate it.
+var LoggingCaveat = security.CaveatDescriptor{
+	Id: uniqueid.Id{
+		176,
+		52,
+		28,
+		237,
+		226,
+		223,
+		129,
+		189,
+		237,
+		112,
+		151,
+		187,
+		85,
+		173,
+		128,
+		0,
+	},
+	ParamType: vdl.TypeOf([]string(nil)),
+}
diff --git a/services/security/roled/internal/config.vdl b/services/security/roled/internal/config.vdl
new file mode 100644
index 0000000..cffe6b4
--- /dev/null
+++ b/services/security/roled/internal/config.vdl
@@ -0,0 +1,26 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import "v.io/v23/security"
+
+// Config contains the attributes of the role, and the list of members who have
+// access to it.
+type Config struct {
+	// Blessings that match at least one of the patterns in this set are
+	// allowed to act on behalf of the role.
+	Members []security.BlessingPattern
+	// Indicates that the blessing name of the caller should be appended to
+	// the role blessing name.
+	Extend bool
+	// If Audit is true, each use of the role blessing will be reported to
+	// an auditing service and will be usable only if the report was
+	// successful.
+	Audit bool
+	// The amount of time for which the role blessing will be valid. It is a
+	// string representation of a time.Duration, e.g. "24h". An empty string
+	// indicates that the role blessing will not expire.
+	Expiry string
+}
diff --git a/services/security/roled/internal/config.vdl.go b/services/security/roled/internal/config.vdl.go
new file mode 100644
index 0000000..258fed3
--- /dev/null
+++ b/services/security/roled/internal/config.vdl.go
@@ -0,0 +1,44 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file was auto-generated by the vanadium vdl tool.
+// Source: config.vdl
+
+package internal
+
+import (
+	// VDL system imports
+	"v.io/v23/vdl"
+
+	// VDL user imports
+	"v.io/v23/security"
+)
+
+// Config contains the attributes of the role, and the list of members who have
+// access to it.
+type Config struct {
+	// Blessings that match at least one of the patterns in this set are
+	// allowed to act on behalf of the role.
+	Members []security.BlessingPattern
+	// Indicates that the blessing name of the caller should be appended to
+	// the role blessing name.
+	Extend bool
+	// If Audit is true, each use of the role blessing will be reported to
+	// an auditing service and will be usable only if the report was
+	// successful.
+	Audit bool
+	// The amount of time for which the role blessing will be valid. It is a
+	// string representation of a time.Duration, e.g. "24h". An empty string
+	// indicates that the role blessing will not expire.
+	Expiry string
+}
+
+func (Config) __VDLReflect(struct {
+	Name string "v.io/x/ref/services/security/roled/internal.Config"
+}) {
+}
+
+func init() {
+	vdl.Register((*Config)(nil))
+}
diff --git a/services/security/roled/internal/discharger.go b/services/security/roled/internal/discharger.go
new file mode 100644
index 0000000..8bcab33
--- /dev/null
+++ b/services/security/roled/internal/discharger.go
@@ -0,0 +1,60 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+	"time"
+
+	"v.io/v23"
+	"v.io/v23/context"
+	"v.io/v23/rpc"
+	"v.io/v23/security"
+	"v.io/v23/verror"
+
+	isecurity "v.io/x/ref/services/security"
+
+	"v.io/x/lib/vlog"
+)
+
+func init() {
+	security.RegisterCaveatValidator(LoggingCaveat, func(ctx *context.T, params []string) error {
+		vlog.Infof("Params: %#v", params)
+		return nil
+	})
+
+}
+
+type discharger struct{}
+
+func (discharger) Discharge(call rpc.ServerCall, caveat security.Caveat, impetus security.DischargeImpetus) (security.Discharge, error) {
+	details := caveat.ThirdPartyDetails()
+	if details == nil {
+		return security.Discharge{}, isecurity.NewErrNotAThirdPartyCaveat(call.Context(), caveat)
+	}
+	if err := details.Dischargeable(call.Context()); err != nil {
+		return security.Discharge{}, err
+	}
+	// TODO(rthellend,ashankar): Do proper logging when the API allows it.
+	vlog.Infof("Discharge() impetus: %#v", impetus)
+
+	expiry, err := security.ExpiryCaveat(time.Now().Add(5 * time.Minute))
+	if err != nil {
+		return security.Discharge{}, verror.Convert(verror.ErrInternal, call.Context(), err)
+	}
+	// Bind the discharge to precisely the purpose the requestor claims it will be used.
+	method, err := security.MethodCaveat(impetus.Method)
+	if err != nil {
+		return security.Discharge{}, verror.Convert(verror.ErrInternal, call.Context(), err)
+	}
+	peer, err := security.NewCaveat(security.PeerBlessingsCaveat, impetus.Server)
+	if err != nil {
+		return security.Discharge{}, verror.Convert(verror.ErrInternal, call.Context(), err)
+	}
+	discharge, err := v23.GetPrincipal(call.Context()).MintDischarge(caveat, expiry, method, peer)
+	if err != nil {
+		return security.Discharge{}, verror.Convert(verror.ErrInternal, call.Context(), err)
+	}
+	return discharge, nil
+}
diff --git a/services/security/roled/internal/dispatcher.go b/services/security/roled/internal/dispatcher.go
new file mode 100644
index 0000000..d01e8a2
--- /dev/null
+++ b/services/security/roled/internal/dispatcher.go
@@ -0,0 +1,102 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"v.io/v23/context"
+	"v.io/v23/rpc"
+	"v.io/v23/security"
+	"v.io/v23/verror"
+
+	isecurity "v.io/x/ref/services/security"
+
+	"v.io/x/lib/vlog"
+)
+
+const requiredSuffix = security.ChainSeparator + isecurity.RoleSuffix
+
+// NewDispatcher returns a dispatcher object for a role service and its
+// associated discharger service.
+// The configRoot is the top level directory where the role configuration files
+// are stored.
+// The dischargerLocation is the object name or address of the discharger
+// service for the third-party caveats attached to the role blessings returned
+// by the role service.
+func NewDispatcher(configRoot, dischargerLocation string) rpc.Dispatcher {
+	return &dispatcher{configRoot, dischargerLocation}
+}
+
+type dispatcher struct {
+	configRoot         string
+	dischargerLocation string
+}
+
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
+	if len(suffix) == 0 {
+		return isecurity.DischargerServer(&discharger{}), &openAuthorizer{}, nil
+	}
+	fileName := filepath.Join(d.configRoot, filepath.FromSlash(suffix+".conf"))
+	if !strings.HasPrefix(fileName, d.configRoot) {
+		// Guard against ".." in the suffix that could be used to read
+		// files outside of the config root.
+		return nil, nil, verror.New(verror.ErrNoExistOrNoAccess, nil)
+	}
+	config, err := loadConfig(fileName)
+	if err != nil && !os.IsNotExist(err) {
+		// The config file exists, but we failed to read it for some
+		// reason. This is likely a server configuration error.
+		vlog.Errorf("loadConfig(%q): %v", fileName, err)
+		return nil, nil, verror.Convert(verror.ErrInternal, nil, err)
+	}
+	obj := &roleService{role: suffix, config: config, dischargerLocation: d.dischargerLocation}
+	return isecurity.RoleServer(obj), &authorizer{config}, nil
+}
+
+type openAuthorizer struct{}
+
+func (openAuthorizer) Authorize(*context.T) error {
+	return nil
+}
+
+type authorizer struct {
+	config *Config
+}
+
+func (a *authorizer) Authorize(ctx *context.T) error {
+	if a.config == nil {
+		return verror.New(verror.ErrNoExistOrNoAccess, ctx)
+	}
+	remoteBlessingNames, _ := security.RemoteBlessingNames(ctx)
+
+	for _, pattern := range a.config.Members {
+		if pattern.MatchedBy(remoteBlessingNames...) {
+			return nil
+		}
+	}
+	return verror.New(verror.ErrNoExistOrNoAccess, ctx)
+}
+
+func loadConfig(fileName string) (*Config, error) {
+	contents, err := ioutil.ReadFile(fileName)
+	if err != nil {
+		return nil, err
+	}
+	var c Config
+	if err := json.Unmarshal(contents, &c); err != nil {
+		return nil, err
+	}
+	for i, pattern := range c.Members {
+		if p := string(pattern); !strings.HasSuffix(p, requiredSuffix) {
+			c.Members[i] = security.BlessingPattern(p + requiredSuffix)
+		}
+	}
+	return &c, nil
+}
diff --git a/services/security/roled/internal/doc.go b/services/security/roled/internal/doc.go
new file mode 100644
index 0000000..118ee40
--- /dev/null
+++ b/services/security/roled/internal/doc.go
@@ -0,0 +1,6 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package internal implements the role service defined in v.io/x/ref/services/security
+package internal
diff --git a/services/security/roled/internal/role.go b/services/security/roled/internal/role.go
new file mode 100644
index 0000000..3941329
--- /dev/null
+++ b/services/security/roled/internal/role.go
@@ -0,0 +1,145 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+	"time"
+
+	"v.io/v23"
+	"v.io/v23/context"
+	"v.io/v23/rpc"
+	"v.io/v23/security"
+	"v.io/v23/verror"
+
+	"v.io/x/lib/vlog"
+)
+
+var (
+	errNoLocalBlessings = verror.Register("v.io/x/ref/services/security/roled/internal/noLocalBlessings", verror.NoRetry, "{1:}{2:} no local blessings")
+)
+
+type roleService struct {
+	role               string
+	config             *Config
+	dischargerLocation string
+}
+
+func (i *roleService) SeekBlessings(call rpc.ServerCall) (security.Blessings, error) {
+	ctx := call.Context()
+	remoteBlessingNames, _ := security.RemoteBlessingNames(ctx)
+	vlog.Infof("%q.SeekBlessings() called by %q", i.role, remoteBlessingNames)
+
+	members := i.filterNonMembers(remoteBlessingNames)
+	if len(members) == 0 {
+		// The Authorizer should already have caught that.
+		return security.Blessings{}, verror.New(verror.ErrNoAccess, ctx)
+	}
+
+	extensions := extensions(i.config, i.role, members)
+	caveats, err := caveats(ctx, i.config)
+	if err != nil {
+		return security.Blessings{}, err
+	}
+
+	return createBlessings(ctx, i.config, v23.GetPrincipal(ctx), extensions, caveats, i.dischargerLocation)
+}
+
+// filterNonMembers returns only the blessing names that are authorized members
+// for the role.
+func (i *roleService) filterNonMembers(blessingNames []string) []string {
+	var results []string
+	for _, name := range blessingNames {
+		// It is not enough to know if the pattern is matched by the
+		// blessings. We need to know exactly which names matched.
+		// These names will be used later to construct the role
+		// blessings.
+		for _, pattern := range i.config.Members {
+			if pattern.MatchedBy(name) {
+				results = append(results, name)
+				break
+			}
+		}
+	}
+	return results
+}
+
+func extensions(config *Config, role string, blessingNames []string) []string {
+	if !config.Extend {
+		return []string{role}
+	}
+	var extensions []string
+	for _, b := range blessingNames {
+		extensions = append(extensions, role+security.ChainSeparator+b)
+	}
+	return extensions
+}
+
+func caveats(ctx *context.T, config *Config) ([]security.Caveat, error) {
+	if config.Expiry == "" {
+		return nil, nil
+	}
+	d, err := time.ParseDuration(config.Expiry)
+	if err != nil {
+		return nil, verror.Convert(verror.ErrInternal, ctx, err)
+	}
+	expiry, err := security.ExpiryCaveat(time.Now().Add(d))
+	if err != nil {
+		return nil, verror.Convert(verror.ErrInternal, ctx, err)
+	}
+	return []security.Caveat{expiry}, nil
+}
+
+func createBlessings(ctx *context.T, config *Config, principal security.Principal, extensions []string, caveats []security.Caveat, dischargerLocation string) (security.Blessings, error) {
+	blessWith := security.GetCall(ctx).LocalBlessings()
+	blessWithNames := security.LocalBlessingNames(ctx)
+	publicKey := security.GetCall(ctx).RemoteBlessings().PublicKey()
+	if len(blessWithNames) == 0 {
+		return security.Blessings{}, verror.New(errNoLocalBlessings, ctx)
+	}
+
+	var ret security.Blessings
+	for _, ext := range extensions {
+		cav := caveats
+		if config.Audit {
+			// TODO(rthellend): This third-party caveat will only work with a single
+			// discharger service. We need a way to allow multiple instances of this
+			// service to be interchangeable.
+
+			fullNames := make([]string, len(blessWithNames))
+			for i, n := range blessWithNames {
+				fullNames[i] = n + security.ChainSeparator + ext
+			}
+			loggingCaveat, err := security.NewCaveat(LoggingCaveat, fullNames)
+			if err != nil {
+				return security.Blessings{}, verror.Convert(verror.ErrInternal, ctx, err)
+			}
+			thirdParty, err := security.NewPublicKeyCaveat(principal.PublicKey(), dischargerLocation, security.ThirdPartyRequirements{true, true, true}, loggingCaveat)
+			if err != nil {
+				return security.Blessings{}, verror.Convert(verror.ErrInternal, ctx, err)
+			}
+			cav = append(cav, thirdParty)
+		}
+		if len(cav) == 0 {
+			// TODO(rthellend,ashankar): the use of unconstrained
+			// use is concerning. We should figure out how to get
+			// rid of it.
+			// Some options:
+			//  - have the seeker specify a set of caveats in the
+			//    request (and forcefully insert a restrictive one
+			//    or fail if the role server thinks that they are
+			//    too loose or something).
+			//  - have a set of caveats in the config of the role.
+			cav = []security.Caveat{security.UnconstrainedUse()}
+		}
+		b, err := principal.Bless(publicKey, blessWith, ext, cav[0], cav[1:]...)
+		if err != nil {
+			return security.Blessings{}, verror.Convert(verror.ErrInternal, ctx, err)
+		}
+		if ret, err = security.UnionOfBlessings(ret, b); err != nil {
+			verror.Convert(verror.ErrInternal, ctx, err)
+		}
+	}
+	return ret, nil
+}
diff --git a/services/security/roled/internal/role_test.go b/services/security/roled/internal/role_test.go
new file mode 100644
index 0000000..3ced6b1
--- /dev/null
+++ b/services/security/roled/internal/role_test.go
@@ -0,0 +1,205 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal_test
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"reflect"
+	"testing"
+
+	"v.io/v23"
+	"v.io/v23/context"
+	"v.io/v23/naming"
+	"v.io/v23/rpc"
+	"v.io/v23/security"
+	"v.io/v23/verror"
+
+	_ "v.io/x/ref/profiles"
+	vsecurity "v.io/x/ref/security"
+	isecurity "v.io/x/ref/services/security"
+	irole "v.io/x/ref/services/security/roled/internal"
+	"v.io/x/ref/test/testutil"
+)
+
+func TestSeekBlessings(t *testing.T) {
+	ctx, shutdown := v23.Init()
+	defer shutdown()
+
+	workdir, err := ioutil.TempDir("", "test-role-server-")
+	if err != nil {
+		t.Fatal("ioutil.TempDir failed: %v", err)
+	}
+	defer os.RemoveAll(workdir)
+
+	// Role A is a restricted role, i.e. it can be used in sensitive ACLs.
+	roleAConf := irole.Config{
+		Members: []security.BlessingPattern{
+			"root/users/user1/_role",
+			"root/users/user2/_role",
+			"root/users/user3", // _role/A implied
+		},
+		Extend: true,
+	}
+	writeConfig(t, roleAConf, filepath.Join(workdir, "A.conf"))
+
+	// Role B is an unrestricted role.
+	roleBConf := irole.Config{
+		Members: []security.BlessingPattern{
+			"root/users/user1/_role",
+			"root/users/user3/_role",
+		},
+		Audit:  true,
+		Extend: false,
+	}
+	writeConfig(t, roleBConf, filepath.Join(workdir, "B.conf"))
+
+	root := testutil.NewIDProvider("root")
+
+	var (
+		user1  = newPrincipalContext(t, ctx, root, "users/user1")
+		user1R = newPrincipalContext(t, ctx, root, "users/user1/_role")
+		user2  = newPrincipalContext(t, ctx, root, "users/user2")
+		user2R = newPrincipalContext(t, ctx, root, "users/user2/_role")
+		user3  = newPrincipalContext(t, ctx, root, "users/user3")
+		user3R = newPrincipalContext(t, ctx, root, "users/user3", "users/user3/_role/foo", "users/user3/_role/bar")
+	)
+
+	testServerCtx := newPrincipalContext(t, ctx, root, "testserver")
+	server, testAddr := newServer(t, testServerCtx)
+	tDisp := &testDispatcher{}
+	if err := server.ServeDispatcher("", tDisp); err != nil {
+		t.Fatalf("server.ServeDispatcher failed: %v", err)
+	}
+
+	const noErr = ""
+	testcases := []struct {
+		ctx       *context.T
+		role      string
+		errID     verror.ID
+		blessings []string
+	}{
+		{user1, "", verror.ErrNoExist.ID, nil},
+		{user1, "unknown", verror.ErrNoAccess.ID, nil},
+		{user2, "unknown", verror.ErrNoAccess.ID, nil},
+		{user3, "unknown", verror.ErrNoAccess.ID, nil},
+
+		{user1, "A", verror.ErrNoAccess.ID, nil},
+		{user1R, "A", noErr, []string{"root/roles/A/root/users/user1/_role"}},
+		{user2, "A", verror.ErrNoAccess.ID, nil},
+		{user2R, "A", noErr, []string{"root/roles/A/root/users/user2/_role"}},
+		{user3, "A", verror.ErrNoAccess.ID, nil},
+		{user3R, "A", noErr, []string{"root/roles/A/root/users/user3/_role/bar", "root/roles/A/root/users/user3/_role/foo"}},
+
+		{user1, "B", verror.ErrNoAccess.ID, nil},
+		{user1R, "B", noErr, []string{"root/roles/B"}},
+		{user2, "B", verror.ErrNoAccess.ID, nil},
+		{user2R, "B", verror.ErrNoAccess.ID, nil},
+		{user3, "B", verror.ErrNoAccess.ID, nil},
+		{user3R, "B", noErr, []string{"root/roles/B"}},
+	}
+	addr := newRoleServer(t, newPrincipalContext(t, ctx, root, "roles"), workdir)
+	for _, tc := range testcases {
+		user := v23.GetPrincipal(tc.ctx).BlessingStore().Default()
+		c := isecurity.RoleClient(naming.Join(addr, tc.role))
+		blessings, err := c.SeekBlessings(tc.ctx)
+		if verror.ErrorID(err) != tc.errID {
+			t.Errorf("unexpected error ID for (%q, %q). Got %#v, expected %#v", user, tc.role, verror.ErrorID(err), tc.errID)
+		}
+		if err == nil {
+			previousBlessings, _ := v23.GetPrincipal(tc.ctx).BlessingStore().Set(blessings, security.AllPrincipals)
+			blessingNames, rejected := callTest(t, tc.ctx, testAddr)
+			if !reflect.DeepEqual(blessingNames, tc.blessings) {
+				t.Errorf("unexpected blessings for (%q, %q). Got %q, expected %q", user, tc.role, blessingNames, tc.blessings)
+			}
+			if len(rejected) != 0 {
+				t.Errorf("unexpected rejected blessings for (%q, %q): %q", user, tc.role, rejected)
+			}
+			v23.GetPrincipal(tc.ctx).BlessingStore().Set(previousBlessings, security.AllPrincipals)
+		}
+	}
+}
+
+func newPrincipalContext(t *testing.T, ctx *context.T, root *testutil.IDProvider, names ...string) *context.T {
+	principal := testutil.NewPrincipal()
+	var blessings []security.Blessings
+	for _, n := range names {
+		blessing, err := root.NewBlessings(principal, n)
+		if err != nil {
+			t.Fatal("root.Bless failed for %q: %v", n, err)
+		}
+		blessings = append(blessings, blessing)
+	}
+	bUnion, err := security.UnionOfBlessings(blessings...)
+	if err != nil {
+		t.Fatal("security.UnionOfBlessings failed: %v", err)
+	}
+	vsecurity.SetDefaultBlessings(principal, bUnion)
+	ctx, err = v23.SetPrincipal(ctx, principal)
+	if err != nil {
+		t.Fatal("v23.SetPrincipal failed: %v", err)
+	}
+	return ctx
+}
+
+func newRoleServer(t *testing.T, ctx *context.T, dir string) string {
+	server, addr := newServer(t, ctx)
+	if err := server.ServeDispatcher("", irole.NewDispatcher(dir, addr)); err != nil {
+		t.Fatalf("ServeDispatcher failed: %v", err)
+	}
+	return addr
+}
+
+func newServer(t *testing.T, ctx *context.T) (rpc.Server, string) {
+	server, err := v23.NewServer(ctx)
+	if err != nil {
+		t.Fatalf("NewServer() failed: %v", err)
+	}
+	spec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
+	endpoints, err := server.Listen(spec)
+	if err != nil {
+		t.Fatalf("Listen(%v) failed: %v", spec, err)
+	}
+	return server, endpoints[0].Name()
+}
+
+func writeConfig(t *testing.T, config irole.Config, fileName string) {
+	mConf, err := json.Marshal(config)
+	if err != nil {
+		t.Fatal("json.MarshalIndent failed: %v", err)
+	}
+	if err := ioutil.WriteFile(fileName, mConf, 0644); err != nil {
+		t.Fatal("ioutil.WriteFile(%q, %q) failed: %v", fileName, string(mConf), err)
+	}
+}
+
+func callTest(t *testing.T, ctx *context.T, addr string) (blessingNames []string, rejected []security.RejectedBlessing) {
+	call, err := v23.GetClient(ctx).StartCall(ctx, addr, "Test", nil)
+	if err != nil {
+		t.Fatalf("StartCall failed: %v", err)
+	}
+	if err := call.Finish(&blessingNames, &rejected); err != nil {
+		t.Fatalf("Finish failed: %v", err)
+	}
+	return
+}
+
+type testDispatcher struct {
+}
+
+func (d *testDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
+	return d, d, nil
+}
+
+func (d *testDispatcher) Authorize(*context.T) error {
+	return nil
+}
+
+func (d *testDispatcher) Test(call rpc.ServerCall) ([]string, []security.RejectedBlessing, error) {
+	blessings, rejected := security.RemoteBlessingNames(call.Context())
+	return blessings, rejected, nil
+}
diff --git a/services/security/roled/main.go b/services/security/roled/main.go
new file mode 100644
index 0000000..997358b
--- /dev/null
+++ b/services/security/roled/main.go
@@ -0,0 +1,57 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+
+	"v.io/v23"
+
+	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/signals"
+	_ "v.io/x/ref/profiles/static"
+	irole "v.io/x/ref/services/security/roled/internal"
+)
+
+var (
+	configDir = flag.String("config_dir", "", "The directory where the role configuration files are stored.")
+	name      = flag.String("name", "", "The name to publish for this service.")
+)
+
+func main() {
+	ctx, shutdown := v23.Init()
+	defer shutdown()
+
+	if len(*configDir) == 0 {
+		fmt.Fprintf(os.Stderr, "--config_dir must be specified\n")
+		os.Exit(1)
+	}
+	if len(*name) == 0 {
+		fmt.Fprintf(os.Stderr, "--name must be specified\n")
+		os.Exit(1)
+	}
+	server, err := v23.NewServer(ctx)
+	if err != nil {
+		vlog.Fatalf("NewServer failed: %v", err)
+	}
+
+	listenSpec := v23.GetListenSpec(ctx)
+	eps, err := server.Listen(listenSpec)
+	if err != nil {
+		vlog.Fatalf("Listen(%v) failed: %v", listenSpec, err)
+	}
+	vlog.Infof("Listening on: %q", eps)
+	if err := server.ServeDispatcher(*name, irole.NewDispatcher(*configDir, *name)); err != nil {
+		vlog.Fatalf("ServeDispatcher(%q) failed: %v", *name, err)
+	}
+	if len(*name) > 0 {
+		fmt.Printf("NAME=%s\n", *name)
+	} else if len(eps) > 0 {
+		fmt.Printf("NAME=%s\n", eps[0].Name())
+	}
+	<-signals.ShutdownOnSignals(ctx)
+}