blob: f72ad244fcb49de41fdbf7b99a6279a562ecd50c [file] [log] [blame]
// Implements an identity manager that maps origins to veyron identities. Each instance
// of wspr is expected to have only one user at a time that will be signed in. In this case,
// the user means the person using the app. Each user may use many different names, which in
// practice will be done by having multiple accounts across many identity providers (i.e google,
// facebook,etc). This is similar to having a master identity that is linked to multiple identities
// in today's technology. For each app/origin, the user may choose which name to provide that app,
// which results in a blessed identity for that app. Each blessed identity will have a different
// private key and ideally, all accounts will share the same private key, but for now they are also
// separate. The identity manager only serializes the mapping of app to account and the account
// information, but not the private keys for each app.
// TODO(bjornick,ataly,ashankar): Have all the accounts share the same private key which will be stored
// in a TPM, so no private key gets serialized to disk.
package security
import (
"crypto/ecdsa"
"crypto/sha256"
"io"
"sync"
"time"
"veyron2"
"veyron2/security"
"veyron2/verror"
"veyron2/vom"
)
// permissions is a set of a permissions given to an app, containing the account
// the app has access to and the caveats associated with it.
type permissions struct {
// The account name that is given to an app.
Account string
Caveats []security.ServiceCaveat
}
// persistentState is the state of the manager that will be persisted to disk.
type persistentState struct {
// A mapping of origins to the permissions provide for the origin (such as
// caveats and the account given to the origin)
Origins map[string]permissions
// A set of accounts that maps from a name to the account.
Accounts map[string]security.PrivateID
}
// Serializer is a factory for managing the readers and writers used by the IDManager
// for serialization and deserialization
type Serializer interface {
// DataWriter returns a writer that is used to write the data portion
// of the IDManager
DataWriter() io.WriteCloser
// SignatureWriter returns a writer that is used to write the signature
// of the serialized data.
SignatureWriter() io.WriteCloser
// DataReader returns a reader that is used to read the serialized data.
// If nil is returned, then there is no seralized data to load.
DataReader() io.Reader
// SignatureReader returns a reader that is used to read the signature of the
// serialized data. If nil is returned, then there is no signature to load.
SignatureReader() io.Reader
}
var OriginDoesNotExist = verror.NotFoundf("origin not found")
// IDManager manages app identities. We only serialize the accounts associated with
// this id manager and the mapping of apps to permissions that they were given.
type IDManager struct {
mu sync.Mutex
state persistentState
// The runtime that will be used to create new identities
rt veyron2.Runtime
serializer Serializer
}
// NewIDManager creates a new IDManager from the reader passed in. serializer can't be nil
func NewIDManager(rt veyron2.Runtime, serializer Serializer) (*IDManager, error) {
result := &IDManager{
rt: rt,
state: persistentState{
Origins: map[string]permissions{},
Accounts: map[string]security.PrivateID{},
},
serializer: serializer,
}
reader := serializer.DataReader()
var hadData bool
hash := sha256.New()
if reader != nil {
hadData = true
if err := vom.NewDecoder(io.TeeReader(reader, hash)).Decode(&result.state); err != nil {
return nil, err
}
}
signed := hash.Sum(nil)
var sig security.Signature
reader = serializer.SignatureReader()
var hadSig bool
if reader != nil {
hadSig = true
if err := vom.NewDecoder(serializer.SignatureReader()).Decode(&sig); err != nil {
return nil, err
}
}
if !hadSig && !hadData {
return result, nil
}
if !ecdsa.Verify(rt.Identity().PublicID().PublicKey(), signed, sig.R, sig.S) {
return nil, verror.NotAuthorizedf("signature verification failed")
}
return result, nil
}
// Save serializes the IDManager to the writer.
func (i *IDManager) save() error {
hash := sha256.New()
writer := i.serializer.DataWriter()
if err := vom.NewEncoder(io.MultiWriter(writer, hash)).Encode(i.state); err != nil {
return err
}
if err := writer.Close(); err != nil {
return err
}
signed := hash.Sum(nil)
signature, err := i.rt.Identity().Sign(signed)
if err != nil {
return err
}
writer = i.serializer.SignatureWriter()
if err := vom.NewEncoder(writer).Encode(signature); err != nil {
return err
}
return writer.Close()
}
// Identity returns the identity for an origin. Returns OriginDoesNotExist if
// there is no identity for the origin.
func (i *IDManager) Identity(origin string) (security.PrivateID, error) {
i.mu.Lock()
defer i.mu.Unlock()
perm, found := i.state.Origins[origin]
if !found {
return nil, OriginDoesNotExist
}
return i.generateBlessedID(origin, perm.Account, perm.Caveats)
}
// AccountsMatching returns a list of accounts that match the given pattern.
func (i *IDManager) AccountsMatching(trustedRoot security.PrincipalPattern) []string {
i.mu.Lock()
defer i.mu.Unlock()
result := []string{}
for name, id := range i.state.Accounts {
if id.PublicID().Match(trustedRoot) {
result = append(result, name)
}
}
return result
}
// AddAccount associates a PrivateID with an account name.
func (i *IDManager) AddAccount(name string, id security.PrivateID) error {
i.mu.Lock()
defer i.mu.Unlock()
old, existed := i.state.Accounts[name]
i.state.Accounts[name] = id
if err := i.save(); err != nil {
delete(i.state.Accounts, name)
if existed {
i.state.Accounts[name] = old
}
return err
}
return nil
}
// AddOrigin adds an origin to the manager linked to a the given account.
func (i *IDManager) AddOrigin(origin string, account string, caveats []security.ServiceCaveat) error {
i.mu.Lock()
defer i.mu.Unlock()
if _, found := i.state.Accounts[account]; !found {
return verror.NotFoundf("unknown account %s", account)
}
old, existed := i.state.Origins[origin]
i.state.Origins[origin] = permissions{account, caveats}
if err := i.save(); err != nil {
delete(i.state.Origins, origin)
if existed {
i.state.Origins[origin] = old
}
return err
}
return nil
}
func (i *IDManager) generateBlessedID(name string, account string, caveats []security.ServiceCaveat) (security.PrivateID, error) {
blessor := i.state.Accounts[account]
if blessor == nil {
return nil, verror.NotFoundf("unknown account %s", account)
}
// The name here is irrelevant, since we'll only be storing the blessed name.
blessee, err := i.rt.NewIdentity(name)
if err != nil {
return nil, err
}
blessed, err := blessor.Bless(blessee.PublicID(), name, 24*time.Hour, caveats)
if err != nil {
return nil, verror.NotAuthorizedf("failed to bless id: %v", err)
}
if blessee, err = blessee.Derive(blessed); err != nil {
return nil, verror.Internalf("failed to derive private id: %v", err)
}
return blessee, nil
}