blob: e8567df9c2cae450ee1bb84c37a5982e14c658f1 [file] [log] [blame]
// 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.
// Functions to create and bless principals.
package main
import (
"fmt"
"path/filepath"
"sync"
"time"
"v.io/v23/security"
libsecurity "v.io/x/ref/lib/security"
"v.io/x/ref/services/agent"
"v.io/x/ref/services/agent/agentlib"
"v.io/x/ref/services/agent/keymgr"
)
type credentials struct {
Name string
Blesser string
Duration string
Files []string
}
const (
credentialsDir = "credentials" // Parent directory of all created credentials.
identityProvider = "playground" // Blessing name of the "identity provider" that blesses all other principals
defaultCredentials = "default" // What codeFile.credentials defaults to if empty
)
var reservedCredentials = []string{identityProvider, "mounttabled", "xproxyd", defaultCredentials}
func isReservedCredential(name string) bool {
for _, c := range reservedCredentials {
if name == c {
return true
}
}
return false
}
type credentialsManager struct {
sync.Mutex
dir string
keyMgr agent.KeyManager
// Map from blessing to socket file path.
socks map[string]string
}
func (cm *credentialsManager) socket(name string) (string, error) {
cm.Lock()
defer cm.Unlock()
return cm.socketLocked(name)
}
// called with cm's lock held.
func (cm *credentialsManager) socketLocked(name string) (string, error) {
sock, ok := cm.socks[name]
if !ok {
return "", fmt.Errorf("principal for blessing \"%s\" doesn't exist", name)
}
return sock, nil
}
func (cm *credentialsManager) principal(name string) (agent.Principal, error) {
cm.Lock()
defer cm.Unlock()
return cm.principalLocked(name)
}
// called with cm's lock held.
func (cm *credentialsManager) principalLocked(name string) (agent.Principal, error) {
sock, err := cm.socketLocked(name)
if err != nil {
return nil, err
}
return agentlib.NewAgentPrincipal(sock, 0)
}
func (cm *credentialsManager) createPrincipal(blesser agent.Principal, name string, expiresAfter time.Duration) error {
cm.Lock()
defer cm.Unlock()
if _, ok := cm.socks[name]; ok {
return fmt.Errorf("principal with blessing name \"%s\" already exists", name)
}
handle, err := cm.keyMgr.NewPrincipal(true)
if err != nil {
return err
}
sockPath := filepath.Join(cm.dir, fmt.Sprintf("sock%d", len(cm.socks)))
if err := cm.keyMgr.ServePrincipal(handle, sockPath); err != nil {
return err
}
cm.socks[name] = sockPath
p, err := cm.principalLocked(name)
if err != nil {
return err
}
defer p.Close()
expiry, err := security.NewExpiryCaveat(time.Now().Add(expiresAfter))
if err != nil {
return err
}
var blessing security.Blessings
if blesser == nil {
// Self-blessed.
if blessing, err = p.BlessSelf(name, expiry); err != nil {
return err
}
} else {
with, _ := blesser.BlessingStore().Default()
if blessing, err = blesser.Bless(p.PublicKey(), with, name, expiry); err != nil {
return err
}
}
return libsecurity.SetDefaultBlessings(p, blessing)
}
func newCredentialsManager(creds []credentials) (*credentialsManager, error) {
keyMgr, err := keymgr.NewLocalAgent(credentialsDir, nil)
if err != nil {
return nil, err
}
credsMgr := &credentialsManager{
dir: credentialsDir,
keyMgr: keyMgr,
socks: make(map[string]string),
}
// Create the root identity provider.
if err := credsMgr.createPrincipal(nil, identityProvider, time.Hour); err != nil {
return nil, err
}
rootPrincipal, err := credsMgr.principal(identityProvider)
if err != nil {
return nil, err
}
defer rootPrincipal.Close()
// Create the other reserved principals.
for _, name := range reservedCredentials {
if name != identityProvider {
if err := credsMgr.createPrincipal(rootPrincipal, name, time.Hour); err != nil {
return nil, err
}
}
}
// Create all the user-specified principals.
for _, cred := range creds {
var blesser agent.Principal
if cred.Blesser == "" {
blesser = rootPrincipal
} else {
blesser, err = credsMgr.principal(cred.Blesser)
if err != nil {
return nil, err
}
defer blesser.Close()
}
expiry := time.Hour
if cred.Duration != "" {
expiry, err = time.ParseDuration(cred.Duration)
if err != nil {
return nil, err
}
}
if err := credsMgr.createPrincipal(blesser, cred.Name, expiry); err != nil {
return nil, err
}
}
return credsMgr, nil
}
func (cm *credentialsManager) Close() error {
return cm.keyMgr.Close()
}