blob: 174830a0993fe18c5a9cf45cc5d8d57b711f233c [file] [log] [blame]
package discharger
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"path/filepath"
"strings"
"sync"
"veyron/security/caveat"
ssecurity "veyron/services/security"
"veyron2/ipc"
"veyron2/naming"
"veyron2/rt"
"veyron2/security"
"veyron2/storage"
"veyron2/storage/vstore"
"veyron2/vlog"
"veyron2/vom"
)
// TODO(ataly, andreser) This package uses a global variable to store the
// revoker state to make it accessible to caveat.Validate. Ideally, we would
// pass this in through the context (or something equivalent).
type revocationServiceT struct {
store storage.Store
pathInStore string
}
var revocationService struct {
*revocationServiceT
sync.Mutex
}
type revocationCaveat [32]byte
func (cav revocationCaveat) Validate(security.Context) error {
// TODO(ashankar,mattr): Figure out how to get the context of an existing RPC here
rctx := rt.R().NewContext()
revocation := revocationService.store.BindObject(
naming.Join(revocationService.pathInStore, hex.EncodeToString(cav[:])))
exists, err := revocation.Exists(rctx)
if err != nil {
return err
}
if exists {
return fmt.Errorf("revoked")
}
return nil
}
// NewRevocationCaveat returns a security.ThirdPartyCaveat that discharger will
// mint discharges until explicitly told not to by calling Revoke on it
// (using the returned revocation token)
func NewRevocationCaveat(dischargerID security.PublicID, dischargerLocation string) (ssecurity.RevocationToken, security.ThirdPartyCaveat, error) {
var revocation ssecurity.RevocationToken
if _, err := rand.Read(revocation[:]); err != nil {
return revocation, nil, err
}
restriction := revocationCaveat(sha256.Sum256(revocation[:]))
cav, err := caveat.NewPublicKeyCaveat(restriction, dischargerID, dischargerLocation)
return revocation, cav, err
}
func (revoceationService *revocationServiceT) Revoke(ctx ipc.ServerContext, caveatPreimage ssecurity.RevocationToken) error {
caveatNonce := sha256.Sum256(caveatPreimage[:])
revocation := revocationService.store.BindObject(naming.Join(revocationService.pathInStore, hex.EncodeToString(caveatNonce[:])))
if _, err := revocation.Put(ctx, caveatPreimage[:]); err != nil {
return err
}
return nil
}
// NewRevoker returns a new revoker service that can be passed to a dispatcher.
// Currently, due to the use of global variables, this function can be called only once.
func NewRevoker(storeName, pathInStore string) (interface{}, error) {
revocationService.Lock()
defer revocationService.Unlock()
if revocationService.revocationServiceT != nil {
return nil, fmt.Errorf("revoker.Revoker called more than once")
}
var err error
revocationService.revocationServiceT = &revocationServiceT{pathInStore: pathInStore}
revocationService.store, err = vstore.New(storeName)
if err != nil {
return nil, err
}
rctx := rt.R().NewContext()
// Transaction is rooted at "", so tname == tid.
tname, err := revocationService.store.BindTransactionRoot("").CreateTransaction(rctx)
if err != nil {
return nil, err
}
// Create parent directories for the revoker root, if necessary
// TODO(tilaks,andreser): provide a `mkdir -p` equivalent in store
l := strings.Split(pathInStore, "/")
fmt.Println(l)
for i := 0; i <= len(l); i++ {
fmt.Println(i, filepath.Join(l[:i]...))
prefix := filepath.Join(l[:i]...)
o := revocationService.store.BindObject(naming.Join(tname, prefix))
if exist, err := o.Exists(rctx); err != nil {
vlog.Infof("Error checking existence at %q: %s", prefix, err)
} else if !exist {
if _, err := o.Put(rctx, &Dir{}); err != nil {
vlog.Infof("Error creating directory %q: %s", prefix, err)
}
}
}
if err := revocationService.store.BindTransaction(tname).Commit(rctx); err != nil {
vlog.Fatalf("Failed to commit creation of revoker root at %s: %s", pathInStore, err)
}
return ssecurity.NewServerRevoker(revocationService.revocationServiceT), nil
}
func init() {
vom.Register(revocationCaveat{})
}