blob: e6a814d12506a82835c8541590a526aa3ffddeaa [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.
package server
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha512"
"crypto/x509"
"encoding/base64"
"fmt"
"io"
"net"
"os"
"path/filepath"
"strconv"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/options"
"v.io/v23/rpc"
"v.io/v23/security"
"v.io/v23/verror"
vsecurity "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/internal/unixfd"
)
const PrincipalHandleByteSize = sha512.Size
const pkgPath = "v.io/x/ref/services/agent/internal/server"
// Errors
var (
errStoragePathRequired = verror.Register(pkgPath+".errStoragePathRequired",
verror.NoRetry, "{1:}{2:} RunKeyManager: storage path is required")
errNotMultiKeyMode = verror.Register(pkgPath+".errNotMultiKeyMode",
verror.NoRetry, "{1:}{2:} Not running in multi-key mode")
)
type keyHandle [PrincipalHandleByteSize]byte
type agentd struct {
id int
w *watchers
principal security.Principal
ctx *context.T
}
type keyData struct {
w *watchers
p security.Principal
}
type keymgr struct {
path string
passphrase []byte
ctx *context.T
}
// RunAnonymousAgent starts the agent server listening on an
// anonymous unix domain socket. It will respond to requests
// using 'principal'.
//
// The returned 'client' and 'endpoint' are typically passed via
// cmd.ExtraFiles and envvar.AgentEndpoint to a child process.
//
// When passing 'endpoint' to a child, set 'remoteFd' to the fd number
// in the child process. If 'endpoint' will be used in this process
// (e.g. in the agent unit tests), set 'remoteFd' to -1.
func RunAnonymousAgent(ctx *context.T, principal security.Principal, remoteFd int) (client *os.File, endpoint string, err error) {
local, remote, err := unixfd.Socketpair()
if err != nil {
return nil, "", err
}
if err = startAgent(ctx, local, newWatchers(), principal); err != nil {
remote.Close()
return nil, "", err
}
if remoteFd == -1 {
remoteFd = int(remote.Fd())
}
return remote, agentlib.AgentEndpoint(remoteFd), nil
}
// RunKeyManager starts the key manager server listening on an anonymous unix
// domain socket. It will persist principals in 'path' using 'passphrase'.
// The returned 'client' is typically passed via cmd.ExtraFiles to a child
// process.
func RunKeyManager(ctx *context.T, path string, passphrase []byte) (client *os.File, err error) {
if path == "" {
return nil, verror.New(errStoragePathRequired, nil)
}
mgr := &keymgr{path: path, passphrase: passphrase, ctx: ctx}
if err := os.MkdirAll(filepath.Join(mgr.path, "keys"), 0700); err != nil {
return nil, err
}
if err := os.MkdirAll(filepath.Join(mgr.path, "creds"), 0700); err != nil {
return nil, err
}
local, client, err := unixfd.Socketpair()
if err != nil {
return nil, err
}
go mgr.readConns(ctx, local)
return client, nil
}
func (a *keymgr) readConns(ctx *context.T, conn *net.UnixConn) {
cache := make(map[keyHandle]keyData)
donech := a.ctx.Done()
if donech != nil {
go func() {
// Shut down our read loop if the context is cancelled
<-donech
conn.Close()
}()
}
defer conn.Close()
var buf keyHandle
for {
addr, n, ack, err := unixfd.ReadConnection(conn, buf[:])
if err == io.EOF {
return
} else if err != nil {
// We ignore read errors, unless the context is cancelled.
select {
case <-donech:
return
default:
ctx.Infof("Error accepting connection: %v", err)
continue
}
}
ack()
var data keyData
if n == len(buf) {
if cached, ok := cache[buf]; ok {
data = cached
} else if data, err = a.readKey(buf); err != nil {
ctx.Error(err)
continue
} else {
cache[buf] = data
}
} else if n == 1 {
if buf, data, err = a.newKey(buf[0] == 1); err != nil {
ctx.Infof("Error creating key: %v", err)
unixfd.CloseUnixAddr(addr)
continue
}
cache[buf] = data
if _, err = conn.Write(buf[:]); err != nil {
ctx.Infof("Error sending key handle: %v", err)
unixfd.CloseUnixAddr(addr)
continue
}
} else {
ctx.Infof("invalid key: %d bytes, expected %d or 1", n, len(buf))
unixfd.CloseUnixAddr(addr)
continue
}
conn, err := dial(addr)
if err != nil {
ctx.Info(err)
continue
}
if err := startAgent(a.ctx, conn, data.w, data.p); err != nil {
ctx.Infof("error starting agent: %v", err)
}
}
}
func (a *keymgr) readKey(handle keyHandle) (keyData, error) {
var nodata keyData
filename := base64.URLEncoding.EncodeToString(handle[:])
in, err := os.Open(filepath.Join(a.path, "keys", filename))
if err != nil {
return nodata, fmt.Errorf("unable to open key file: %v", err)
}
defer in.Close()
key, err := vsecurity.LoadPEMKey(in, a.passphrase)
if err != nil {
return nodata, fmt.Errorf("unable to load key in %q: %v", in.Name(), err)
}
state, err := vsecurity.NewPrincipalStateSerializer(filepath.Join(a.path, "creds", filename))
if err != nil {
return nodata, fmt.Errorf("unable to create persisted state serializer: %v", err)
}
principal, err := vsecurity.NewPrincipalFromSigner(security.NewInMemoryECDSASigner(key.(*ecdsa.PrivateKey)), state)
if err != nil {
return nodata, fmt.Errorf("unable to load principal: %v", err)
}
return keyData{newWatchers(), principal}, nil
}
func dial(addr net.Addr) (*net.UnixConn, error) {
fd, err := strconv.ParseInt(addr.String(), 10, 32)
if err != nil {
return nil, fmt.Errorf("invalid address: %v", addr)
}
file := os.NewFile(uintptr(fd), "client")
defer file.Close()
conn, err := net.FileConn(file)
if err != nil {
return nil, fmt.Errorf("unable to create conn: %v", err)
}
return conn.(*net.UnixConn), nil
}
func startAgent(ctx *context.T, conn *net.UnixConn, w *watchers, principal security.Principal) error {
donech := ctx.Done()
if donech != nil {
go func() {
// Interrupt the read loop if the context is cancelled.
<-donech
conn.Close()
}()
}
go func() {
buf := make([]byte, 1)
for {
clientAddr, _, ack, err := unixfd.ReadConnection(conn, buf)
if err == io.EOF {
conn.Close()
return
} else if err != nil {
// We ignore read errors, unless the context is cancelled.
select {
case <-donech:
return
default:
ctx.Infof("Error accepting connection: %v", err)
continue
}
}
if clientAddr != nil {
// SecurityNone is safe since we're using anonymous unix sockets.
// Only our child process can possibly communicate on the socket.
//
// Also, SecurityNone implies that s (rpc.Server) created below does not
// authenticate to clients, so runtime.Principal is irrelevant for the agent.
// TODO(ribrdb): Shutdown these servers when the connection is closed.
s, err := v23.NewServer(ctx, options.SecurityNone)
if err != nil {
ctx.Infof("Error creating server: %v", err)
ack()
continue
}
a := []struct{ Protocol, Address string }{
{clientAddr.Network(), clientAddr.String()},
}
spec := rpc.ListenSpec{Addrs: rpc.ListenAddrs(a)}
if _, err = s.Listen(spec); err == nil {
server := agent.AgentServer(&agentd{w.newID(), w, principal, ctx})
err = s.Serve("", server, nil)
}
ack()
}
if err != nil {
ctx.Infof("Error accepting connection: %v", err)
}
}
}()
return nil
}
func (a agentd) Bless(_ *context.T, _ rpc.ServerCall, key []byte, with security.Blessings, extension string, caveat security.Caveat, additionalCaveats []security.Caveat) (security.Blessings, error) {
pkey, err := security.UnmarshalPublicKey(key)
if err != nil {
return security.Blessings{}, err
}
return a.principal.Bless(pkey, with, extension, caveat, additionalCaveats...)
}
func (a agentd) BlessSelf(_ *context.T, _ rpc.ServerCall, name string, caveats []security.Caveat) (security.Blessings, error) {
return a.principal.BlessSelf(name, caveats...)
}
func (a agentd) Sign(_ *context.T, _ rpc.ServerCall, message []byte) (security.Signature, error) {
return a.principal.Sign(message)
}
func (a agentd) MintDischarge(_ *context.T, _ rpc.ServerCall, forCaveat, caveatOnDischarge security.Caveat, additionalCaveatsOnDischarge []security.Caveat) (security.Discharge, error) {
return a.principal.MintDischarge(forCaveat, caveatOnDischarge, additionalCaveatsOnDischarge...)
}
func (a *keymgr) newKey(in_memory bool) (keyHandle, keyData, error) {
var handle keyHandle
var nodata keyData
if a.path == "" {
return handle, nodata, verror.New(errNotMultiKeyMode, nil)
}
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return handle, nodata, err
}
if handle, err = keyid(key); err != nil {
return handle, nodata, err
}
signer := security.NewInMemoryECDSASigner(key)
var p security.Principal
if in_memory {
if p, err = vsecurity.NewPrincipalFromSigner(signer, nil); err != nil {
return handle, nodata, err
}
} else {
filename := base64.URLEncoding.EncodeToString(handle[:])
out, err := os.OpenFile(filepath.Join(a.path, "keys", filename), os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return handle, nodata, err
}
defer out.Close()
err = vsecurity.SavePEMKey(out, key, a.passphrase)
if err != nil {
return handle, nodata, err
}
state, err := vsecurity.NewPrincipalStateSerializer(filepath.Join(a.path, "creds", filename))
if err != nil {
return handle, nodata, err
}
p, err = vsecurity.NewPrincipalFromSigner(signer, state)
if err != nil {
return handle, nodata, err
}
}
return handle, keyData{newWatchers(), p}, nil
}
func keyid(key *ecdsa.PrivateKey) (handle keyHandle, err error) {
slice, err := x509.MarshalPKIXPublicKey(&key.PublicKey)
if err != nil {
return
}
return sha512.Sum512(slice), nil
}
func (a agentd) PublicKey(_ *context.T, _ rpc.ServerCall) ([]byte, error) {
return a.principal.PublicKey().MarshalBinary()
}
func (a agentd) BlessingsByName(_ *context.T, _ rpc.ServerCall, name security.BlessingPattern) ([]security.Blessings, error) {
a.w.rlock()
defer a.w.runlock()
return a.principal.BlessingsByName(name), nil
}
func (a agentd) BlessingsInfo(_ *context.T, _ rpc.ServerCall, blessings security.Blessings) (map[string][]security.Caveat, error) {
a.w.rlock()
defer a.w.runlock()
return a.principal.BlessingsInfo(blessings), nil
}
func (a agentd) AddToRoots(_ *context.T, _ rpc.ServerCall, blessings security.Blessings) error {
a.w.lock()
defer a.w.unlock(a.id)
return a.principal.AddToRoots(blessings)
}
func (a agentd) BlessingStoreSet(_ *context.T, _ rpc.ServerCall, blessings security.Blessings, forPeers security.BlessingPattern) (security.Blessings, error) {
a.w.lock()
defer a.w.unlock(a.id)
return a.principal.BlessingStore().Set(blessings, forPeers)
}
func (a agentd) BlessingStoreForPeer(_ *context.T, _ rpc.ServerCall, peerBlessings []string) (security.Blessings, error) {
a.w.rlock()
defer a.w.runlock()
return a.principal.BlessingStore().ForPeer(peerBlessings...), nil
}
func (a agentd) BlessingStoreSetDefault(_ *context.T, _ rpc.ServerCall, blessings security.Blessings) error {
a.w.lock()
defer a.w.unlock(a.id)
return a.principal.BlessingStore().SetDefault(blessings)
}
func (a agentd) BlessingStorePeerBlessings(_ *context.T, _ rpc.ServerCall) (map[security.BlessingPattern]security.Blessings, error) {
a.w.rlock()
defer a.w.runlock()
return a.principal.BlessingStore().PeerBlessings(), nil
}
func (a agentd) BlessingStoreDebugString(_ *context.T, _ rpc.ServerCall) (string, error) {
a.w.rlock()
defer a.w.runlock()
return a.principal.BlessingStore().DebugString(), nil
}
func (a agentd) BlessingStoreDefault(_ *context.T, _ rpc.ServerCall) (security.Blessings, error) {
a.w.rlock()
defer a.w.runlock()
return a.principal.BlessingStore().Default(), nil
}
func (a agentd) BlessingStoreCacheDischarge(_ *context.T, _ rpc.ServerCall, discharge security.Discharge, caveat security.Caveat, impetus security.DischargeImpetus) error {
a.w.lock()
a.principal.BlessingStore().CacheDischarge(discharge, caveat, impetus)
a.w.unlock(a.id)
return nil
}
func (a agentd) BlessingStoreClearDischarges(_ *context.T, _ rpc.ServerCall, discharges []security.Discharge) error {
a.w.lock()
a.principal.BlessingStore().ClearDischarges(discharges...)
a.w.unlock(a.id)
return nil
}
func (a agentd) BlessingStoreDischarge(_ *context.T, _ rpc.ServerCall, caveat security.Caveat, impetus security.DischargeImpetus) (security.Discharge, error) {
a.w.lock()
defer a.w.unlock(a.id)
return a.principal.BlessingStore().Discharge(caveat, impetus), nil
}
func (a agentd) BlessingRootsAdd(_ *context.T, _ rpc.ServerCall, root []byte, pattern security.BlessingPattern) error {
pkey, err := security.UnmarshalPublicKey(root)
if err != nil {
return err
}
a.w.lock()
defer a.w.unlock(a.id)
return a.principal.Roots().Add(pkey, pattern)
}
func (a agentd) BlessingRootsRecognized(_ *context.T, _ rpc.ServerCall, root []byte, blessing string) error {
pkey, err := security.UnmarshalPublicKey(root)
if err != nil {
return err
}
a.w.rlock()
defer a.w.runlock()
return a.principal.Roots().Recognized(pkey, blessing)
}
func (a agentd) BlessingRootsDump(_ *context.T, _ rpc.ServerCall) (map[security.BlessingPattern][][]byte, error) {
ret := make(map[security.BlessingPattern][][]byte)
a.w.rlock()
defer a.w.runlock()
for p, keys := range a.principal.Roots().Dump() {
for _, key := range keys {
marshaledKey, err := key.MarshalBinary()
if err != nil {
return nil, err
}
ret[p] = append(ret[p], marshaledKey)
}
}
return ret, nil
}
func (a agentd) BlessingRootsDebugString(_ *context.T, _ rpc.ServerCall) (string, error) {
a.w.rlock()
defer a.w.runlock()
return a.principal.Roots().DebugString(), nil
}
func (a agentd) NotifyWhenChanged(ctx *context.T, call agent.AgentNotifyWhenChangedServerCall) error {
ch := a.w.register(a.id)
defer a.w.unregister(a.id, ch)
for {
select {
case <-a.ctx.Done():
return nil
case <-ctx.Done():
return nil
case _, ok := <-ch:
if !ok {
return nil
}
if err := call.SendStream().Send(true); err != nil {
return err
}
}
}
}