blob: 3f9ed67e2812933637564001d9751bc3231bdd4d [file] [log] [blame]
package main
import (
"bytes"
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"errors"
"fmt"
"io"
"os"
"os/user"
"time"
"veyron.io/lib/cmdline"
profile "veyron.io/veyron/veyron/profiles/static"
vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron/services/identity"
"veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vom"
)
var (
// Flags for the "blessself" command
flagBlessSelfFor time.Duration
// Flags for the "bless" command
flagBlessFor time.Duration
flagBlessWith string
flagBlessRemoteKey string
flagBlessRemoteToken string
// Flags for the "fork" command
flagForkFor time.Duration
flagForkWith string
// Flags for the "seekblessings" command
flagSeekBlessingsFrom string
flagSeekBlessingsSetDefault bool
flagSeekBlessingsForPeer string
flagSeekBlessingsBrowser bool
// Flags common to many commands
flagAddToRoots bool
flagCreateOverwrite bool
// Flags for the "recvblessings" command
flagRecvBlessingsSetDefault bool
flagRecvBlessingsForPeer string
cmdDump = &cmdline.Command{
Name: "dump",
Short: "Dump out information about the principal",
Long: `
Prints out information about the principal specified by the environment
that this tool is running in.
`,
Run: func(cmd *cmdline.Command, args []string) error {
runtime, err := rt.New()
if err != nil {
panic(err)
}
defer runtime.Cleanup()
p := runtime.Principal()
fmt.Printf("Public key : %v\n", p.PublicKey())
fmt.Println("---------------- BlessingStore ----------------")
fmt.Printf("%v", p.BlessingStore().DebugString())
fmt.Println("---------------- BlessingRoots ----------------")
fmt.Printf("%v", p.Roots().DebugString())
return nil
},
}
cmdDumpBlessings = &cmdline.Command{
Name: "dumpblessings",
Short: "Dump out information about the provided blessings",
Long: `
Prints out information about the blessings (typically obtained from this tool)
encoded in the provided file.
`,
ArgsName: "<file>",
ArgsLong: `
<file> is the path to a file containing blessings typically obtained from
this tool. - is used for STDIN.
`,
Run: func(cmd *cmdline.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("requires exactly one argument, <file>, provided %d", len(args))
}
blessings, err := decodeBlessings(args[0])
if err != nil {
return fmt.Errorf("failed to decode provided blessings: %v", err)
}
wire := security.MarshalBlessings(blessings)
fmt.Printf("Blessings : %v\n", blessings)
fmt.Printf("PublicKey : %v\n", blessings.PublicKey())
fmt.Printf("Certificate chains : %d\n", len(wire.CertificateChains))
for idx, chain := range wire.CertificateChains {
fmt.Printf("Chain #%d (%d certificates). Root certificate public key: %v\n", idx, len(chain), rootkey(chain))
for certidx, cert := range chain {
fmt.Printf(" Certificate #%d: %v with ", certidx, cert.Extension)
switch n := len(cert.Caveats); n {
case 1:
fmt.Printf("1 caveat")
default:
fmt.Printf("%d caveats", n)
}
fmt.Println("")
for cavidx, cav := range cert.Caveats {
fmt.Printf(" (%d) %v\n", cavidx, &cav)
}
}
}
return nil
},
}
cmdBlessSelf = &cmdline.Command{
Name: "blessself",
Short: "Generate a self-signed blessing",
Long: `
Returns a blessing with name <name> and self-signed by the principal specified
by the environment that this tool is running in. Optionally, the blessing can
be restricted with an expiry caveat specified using the --for flag.
`,
ArgsName: "[<name>]",
ArgsLong: `
<name> is the name used to create the self-signed blessing. If not
specified, a name will be generated based on the hostname of the
machine and the name of the user running this command.
`,
Run: func(cmd *cmdline.Command, args []string) error {
var name string
switch len(args) {
case 0:
name = defaultBlessingName()
case 1:
name = args[0]
default:
return fmt.Errorf("requires at most one argument, provided %d", len(args))
}
var caveats []security.Caveat
if flagBlessSelfFor != 0 {
cav, err := security.ExpiryCaveat(time.Now().Add(flagBlessSelfFor))
if err != nil {
return fmt.Errorf("failed to create expiration Caveat: %v", err)
}
caveats = append(caveats, cav)
}
runtime, err := rt.New()
if err != nil {
panic(err)
}
defer runtime.Cleanup()
blessing, err := runtime.Principal().BlessSelf(name, caveats...)
if err != nil {
return fmt.Errorf("failed to create self-signed blessing for name %q: %v", name, err)
}
return dumpBlessings(blessing)
},
}
cmdBless = &cmdline.Command{
Name: "bless",
Short: "Bless another principal",
Long: `
Bless another principal.
The blesser is obtained from the runtime this tool is using. The blessing that
will be extended is the default one from the blesser's store, or specified by
the --with flag. Caveats on the blessing are controlled via the --for flag.
For example, let's say a principal "alice" wants to bless another principal "bob"
as "alice/friend", the invocation would be:
VEYRON_CREDENTIALS=<path to alice> principal bless <path to bob> friend
and this will dump the blessing to STDOUT.
With the --remote_key and --remote_token flags, this command can be used to
bless a principal on a remote machine as well. In this case, the blessing is
not dumped to STDOUT but sent to the remote end. Use 'principal help
recvblessings' for more details on that.
`,
ArgsName: "<principal to bless> <extension>",
ArgsLong: `
<principal to bless> represents the principal to be blessed (i.e., whose public
key will be provided with a name). This can be either:
(a) The directory containing credentials for that principal,
OR
(b) The filename (- for STDIN) containing any other blessings of that
principal,
OR
(c) The object name produced by the 'recvblessings' command of this tool
running on behalf of another principal (if the --remote_key and
--remote_token flags are specified).
<extension> is the string extension that will be applied to create the
blessing.
`,
Run: func(cmd *cmdline.Command, args []string) error {
if len(args) != 2 {
return fmt.Errorf("require exactly two arguments, provided %d", len(args))
}
runtime, err := rt.New()
if err != nil {
panic(err)
}
defer runtime.Cleanup()
p := runtime.Principal()
var with security.Blessings
var caveats []security.Caveat
if len(flagBlessWith) > 0 {
if with, err = decodeBlessings(flagBlessWith); err != nil {
return fmt.Errorf("failed to read blessings from --with=%q: %v", flagBlessWith, err)
}
} else {
with = p.BlessingStore().Default()
}
if c, err := security.ExpiryCaveat(time.Now().Add(flagBlessFor)); err != nil {
return fmt.Errorf("failed to create ExpiryCaveat: %v", err)
} else {
caveats = append(caveats, c)
}
// TODO(ashankar,ataly,suharshs): Work out how to add additional caveats, like maybe
// revocation, method etc.
tobless, extension := args[0], args[1]
if (len(flagBlessRemoteKey) == 0) != (len(flagBlessRemoteToken) == 0) {
return fmt.Errorf("either both --remote_key and --remote_token should be set, or neither should")
}
if len(flagBlessRemoteKey) > 0 {
// Send blessings to a "server" started by a "recvblessings" command
granter := &granter{p, with, extension, caveats, flagBlessRemoteKey}
return sendBlessings(runtime, tobless, granter, flagBlessRemoteToken)
}
// Blessing a principal whose key is available locally.
var key security.PublicKey
if finfo, err := os.Stat(tobless); err == nil && finfo.IsDir() {
other, err := vsecurity.LoadPersistentPrincipal(tobless, nil)
if err != nil {
if other, err = vsecurity.CreatePersistentPrincipal(tobless, nil); err != nil {
return fmt.Errorf("failed to read principal in directory %q: %v", tobless, err)
}
}
key = other.PublicKey()
} else if other, err := decodeBlessings(tobless); err != nil {
return fmt.Errorf("failed to decode blessings in %q: %v", tobless, err)
} else {
key = other.PublicKey()
}
blessings, err := p.Bless(key, with, extension, caveats[0], caveats[1:]...)
if err != nil {
return fmt.Errorf("Bless(%v, %v, %q, ...) failed: %v", key, with, extension, err)
}
return dumpBlessings(blessings)
},
}
cmdStoreForPeer = &cmdline.Command{
Name: "forpeer",
Short: "Return blessings marked for the provided peer",
Long: `
Returns blessings that are marked for the provided peer in the
BlessingStore specified by the environment that this tool is
running in.
`,
ArgsName: "[<peer_1> ... <peer_k>]",
ArgsLong: `
<peer_1> ... <peer_k> are the (human-readable string) blessings bound
to the peer. The returned blessings are marked with a pattern that is
matched by at least one of these. If no arguments are specified,
store.forpeer returns the blessings that are marked for all peers (i.e.,
blessings set on the store with the "..." pattern).
`,
Run: func(cmd *cmdline.Command, args []string) error {
runtime, err := rt.New()
if err != nil {
panic(err)
}
defer runtime.Cleanup()
return dumpBlessings(runtime.Principal().BlessingStore().ForPeer(args...))
},
}
cmdStoreDefault = &cmdline.Command{
Name: "default",
Short: "Return blessings marked as default",
Long: `
Returns blessings that are marked as default in the BlessingStore specified by
the environment that this tool is running in.
`,
Run: func(cmd *cmdline.Command, args []string) error {
runtime, err := rt.New()
if err != nil {
panic(err)
}
defer runtime.Cleanup()
return dumpBlessings(runtime.Principal().BlessingStore().Default())
},
}
cmdStoreSet = &cmdline.Command{
Name: "set",
Short: "Set provided blessings for peer",
Long: `
Marks the provided blessings to be shared with the provided peers on the
BlessingStore specified by the environment that this tool is running in.
'set b pattern' marks the intention to reveal b to peers who
present blessings of their own matching 'pattern'.
'set nil pattern' can be used to remove the blessings previously
associated with the pattern (by a prior 'set' command).
It is an error to call 'store.set' with blessings whose public
key does not match the public key of this principal specified
by the environment.
`,
ArgsName: "<file> <pattern>",
ArgsLong: `
<file> is the path to a file containing a blessing typically obtained
from this tool. - is used for STDIN.
<pattern> is the BlessingPattern used to identify peers with whom this
blessing can be shared with.
`,
Run: func(cmd *cmdline.Command, args []string) error {
if len(args) != 2 {
return fmt.Errorf("requires exactly two arguments <file>, <pattern>, provided %d", len(args))
}
blessings, err := decodeBlessings(args[0])
if err != nil {
return fmt.Errorf("failed to decode provided blessings: %v", err)
}
pattern := security.BlessingPattern(args[1])
runtime, err := rt.New()
if err != nil {
panic(err)
}
defer runtime.Cleanup()
p := runtime.Principal()
if _, err := p.BlessingStore().Set(blessings, pattern); err != nil {
return fmt.Errorf("failed to set blessings %v for peers %v: %v", blessings, pattern, err)
}
if flagAddToRoots {
if err := p.AddToRoots(blessings); err != nil {
return fmt.Errorf("AddToRoots failed: %v", err)
}
}
return nil
},
}
cmdStoreAddToRoots = &cmdline.Command{
Name: "addtoroots",
Short: "Add provided blessings to root set",
Long: `
Adds the provided blessings to the set of trusted roots for this principal.
'addtoroots b' adds blessings b to the trusted root set.
For example, to make the principal in credentials directory A trust the
root of the default blessing in credentials directory B:
principal -veyron.credentials=B bless A some_extension |
principal -veyron.credentials=A store addtoroots -
The extension 'some_extension' has no effect in the command above.
`,
ArgsName: "<file>",
ArgsLong: `
<file> is the path to a file containing a blessing typically obtained
from this tool. - is used for STDIN.
`,
Run: func(cmd *cmdline.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("requires exactly one argument <file>, provided %d", len(args))
}
blessings, err := decodeBlessings(args[0])
if err != nil {
return fmt.Errorf("failed to decode provided blessings: %v", err)
}
runtime, err := rt.New()
if err != nil {
panic(err)
}
defer runtime.Cleanup()
p := runtime.Principal()
if err := p.AddToRoots(blessings); err != nil {
return fmt.Errorf("AddToRoots failed: %v", err)
}
return nil
},
}
cmdStoreSetDefault = &cmdline.Command{
Name: "setdefault",
Short: "Set provided blessings as default",
Long: `
Sets the provided blessings as default in the BlessingStore specified by the
environment that this tool is running in.
It is an error to call 'store.setdefault' with blessings whose public key does
not match the public key of the principal specified by the environment.
`,
ArgsName: "<file>",
ArgsLong: `
<file> is the path to a file containing a blessing typically obtained from
this tool. - is used for STDIN.
`,
Run: func(cmd *cmdline.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("requires exactly one argument, <file>, provided %d", len(args))
}
blessings, err := decodeBlessings(args[0])
if err != nil {
return fmt.Errorf("failed to decode provided blessings: %v", err)
}
runtime, err := rt.New()
if err != nil {
panic(err)
}
defer runtime.Cleanup()
p := runtime.Principal()
if err := p.BlessingStore().SetDefault(blessings); err != nil {
return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
}
if flagAddToRoots {
if err := p.AddToRoots(blessings); err != nil {
return fmt.Errorf("AddToRoots failed: %v", err)
}
}
return nil
},
}
cmdCreate = &cmdline.Command{
Name: "create",
Short: "Create a new principal and persist it into a directory",
Long: `
Creates a new principal with a single self-blessed blessing and writes it out
to the provided directory. The same directory can then be used to set the
VEYRON_CREDENTIALS environment variable for other veyron applications.
The operation fails if the directory already contains a principal. In this case
the --overwrite flag can be provided to clear the directory and write out the
new principal.
`,
ArgsName: "<directory> <blessing>",
ArgsLong: `
<directory> is the directory to which the new principal will be persisted.
<blessing> is the self-blessed blessing that the principal will be setup to use by default.
`,
Run: func(cmd *cmdline.Command, args []string) error {
if len(args) != 2 {
return fmt.Errorf("requires exactly two arguments: <directory> and <blessing>, provided %d", len(args))
}
dir, name := args[0], args[1]
if flagCreateOverwrite {
if err := os.RemoveAll(dir); err != nil {
return err
}
}
p, err := vsecurity.CreatePersistentPrincipal(dir, nil)
if err != nil {
return err
}
blessings, err := p.BlessSelf(name)
if err != nil {
return fmt.Errorf("BlessSelf(%q) failed: %v", name, err)
}
if err := vsecurity.SetDefaultBlessings(p, blessings); err != nil {
return fmt.Errorf("could not set blessings %v as default: %v", blessings, err)
}
return nil
},
}
cmdFork = &cmdline.Command{
Name: "fork",
Short: "Fork a new principal from the principal that this tool is running as and persist it into a directory",
Long: `
Creates a new principal with a blessing from the principal specified by the
environment that this tool is running in, and writes it out to the provided
directory. The blessing that will be extended is the default one from the
blesser's store, or specified by the --with flag. Caveats on the blessing
are controlled via the --for flag. The blessing is marked as default and
shareable with all peers on the new principal's blessing store.
The operation fails if the directory already contains a principal. In this case
the --overwrite flag can be provided to clear the directory and write out the
forked principal.
`,
ArgsName: "<directory> <extension>",
ArgsLong: `
<directory> is the directory to which the forked principal will be persisted.
<extension> is the extension under which the forked principal is blessed.
`,
Run: func(cmd *cmdline.Command, args []string) error {
if len(args) != 2 {
return fmt.Errorf("requires exactly two arguments: <directory> and <extension>, provided %d", len(args))
}
dir, extension := args[0], args[1]
if flagCreateOverwrite {
if err := os.RemoveAll(dir); err != nil {
return err
}
}
p, err := vsecurity.CreatePersistentPrincipal(dir, nil)
if err != nil {
return err
}
runtime, err := rt.New()
if err != nil {
return err
}
defer runtime.Cleanup()
var (
with security.Blessings
caveats []security.Caveat
)
if len(flagForkWith) > 0 {
if with, err = decodeBlessings(flagForkWith); err != nil {
return fmt.Errorf("failed to read blessings from --with=%q: %v", flagForkWith, err)
}
} else {
with = runtime.Principal().BlessingStore().Default()
}
if c, err := security.ExpiryCaveat(time.Now().Add(flagForkFor)); err != nil {
return fmt.Errorf("failed to create ExpiryCaveat: %v", err)
} else {
caveats = append(caveats, c)
}
// TODO(ashankar,ataly,suharshs): Work out how to add additional caveats, like maybe
// revocation, method etc.
key := p.PublicKey()
blessings, err := runtime.Principal().Bless(key, with, extension, caveats[0], caveats[1:]...)
if err != nil {
return fmt.Errorf("Bless(%v, %v, %q, ...) failed: %v", key, with, extension, err)
}
if err := vsecurity.SetDefaultBlessings(p, blessings); err != nil {
return fmt.Errorf("could not set blessings %v as default: %v", blessings, err)
}
return nil
},
}
cmdSeekBlessings = &cmdline.Command{
Name: "seekblessings",
Short: "Seek blessings from a web-based Veyron blessing service",
Long: `
Seeks blessings from a web-based Veyron blesser which
requires the caller to first authenticate with Google using OAuth. Simply
run the command to see what happens.
The blessings are sought for the principal specified by the environment that
this tool is running in.
The blessings obtained are set as default, unless the --set_default flag is
set to true, and are also set for sharing with all peers, unless a more
specific peer pattern is provided using the --for_peer flag.
`,
Run: func(cmd *cmdline.Command, args []string) error {
// Initialize the runtime first so that any local errors are reported
// before the HTTP roundtrips for obtaining the macaroon begin.
runtime, err := rt.New()
if err != nil {
panic(err)
}
defer runtime.Cleanup()
blessedChan := make(chan string)
defer close(blessedChan)
macaroonChan, err := getMacaroonForBlessRPC(flagSeekBlessingsFrom, blessedChan, flagSeekBlessingsBrowser)
if err != nil {
return fmt.Errorf("failed to get macaroon from Veyron blesser: %v", err)
}
macaroon := <-macaroonChan
service := <-macaroonChan
ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
var reply security.WireBlessings
reply, err = identity.MacaroonBlesserClient(service).Bless(ctx, macaroon)
if err != nil {
return fmt.Errorf("failed to get blessing from %q: %v", service, err)
}
blessings, err := security.NewBlessings(reply)
if err != nil {
return fmt.Errorf("failed to construct Blessings object from response: %v", err)
}
blessedChan <- fmt.Sprint(blessings)
// Wait for getTokenForBlessRPC to clean up:
<-macaroonChan
if flagSeekBlessingsSetDefault {
if err := runtime.Principal().BlessingStore().SetDefault(blessings); err != nil {
return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
}
}
if pattern := security.BlessingPattern(flagSeekBlessingsForPeer); len(pattern) > 0 {
if _, err := runtime.Principal().BlessingStore().Set(blessings, pattern); err != nil {
return fmt.Errorf("failed to set blessings %v for peers %v: %v", blessings, pattern, err)
}
}
if flagAddToRoots {
if err := runtime.Principal().AddToRoots(blessings); err != nil {
return fmt.Errorf("AddToRoots failed: %v", err)
}
}
return dumpBlessings(blessings)
},
}
cmdRecvBlessings = &cmdline.Command{
Name: "recvblessings",
Short: "Receive blessings sent by another principal and use them as the default",
Long: `
Allow another principal (likely a remote process) to bless this one.
This command sets up the invoker (this process) to wait for a blessing
from another invocation of this tool (remote process) and prints out the
command to be run as the remote principal.
The received blessings are set as default, unless the --set_default flag is
set to true, and are also set for sharing with all peers, unless a more
specific peer pattern is provided using the --for_peer flag.
TODO(ashankar,cnicolaou): Make this next paragraph possible! Requires
the ability to obtain the proxied endpoint.
Typically, this command should require no arguments.
However, if the sender and receiver are on different network domains, it may
make sense to use the --veyron.proxy flag:
principal --veyron.proxy=proxy recvblessings
The command to be run at the sender is of the form:
principal bless --remote_key=KEY --remote_token=TOKEN ADDRESS
The --remote_key flag is used to by the sender to "authenticate" the receiver,
ensuring it blesses the intended recipient and not any attacker that may have
taken over the address.
The --remote_token flag is used by the sender to authenticate itself to the
receiver. This helps ensure that the receiver rejects blessings from senders
who just happened to guess the network address of the 'recvblessings'
invocation.
`,
Run: func(cmd *cmdline.Command, args []string) error {
if len(args) != 0 {
return fmt.Errorf("command accepts no arguments")
}
runtime, err := rt.New()
if err != nil {
panic(err)
}
defer runtime.Cleanup()
server, err := runtime.NewServer()
if err != nil {
return fmt.Errorf("failed to create server to listen for blessings: %v", err)
}
defer server.Stop()
eps, err := server.Listen(profile.ListenSpec)
if err != nil {
return fmt.Errorf("failed to setup listening: %v", err)
}
var token [24]byte
if _, err := rand.Read(token[:]); err != nil {
return fmt.Errorf("unable to generate token: %v", err)
}
service := &recvBlessingsService{
principal: runtime.Principal(),
token: base64.URLEncoding.EncodeToString(token[:]),
notify: make(chan error),
}
if err := server.Serve("", service, allowAnyone{}); err != nil {
return fmt.Errorf("failed to setup service: %v", err)
}
// Proposed name:
extension := fmt.Sprintf("extension%d", int(token[0])<<16|int(token[1])<<8|int(token[2]))
fmt.Println("Run the following command on behalf of the principal that will send blessings:")
fmt.Println("You may want to adjust flags affecting the caveats on this blessing, for example using")
fmt.Println("the --for flag, or change the extension to something more meaningful")
fmt.Println()
fmt.Printf("principal bless --remote_key=%v --remote_token=%v %v %v\n", runtime.Principal().PublicKey(), service.token, naming.JoinAddressName(eps[0].String(), ""), extension)
fmt.Println()
fmt.Println("...waiting for sender..")
return <-service.notify
},
}
)
func main() {
cmdBlessSelf.Flags.DurationVar(&flagBlessSelfFor, "for", 0, "Duration of blessing validity (zero means no that the blessing is always valid)")
cmdFork.Flags.BoolVar(&flagCreateOverwrite, "overwrite", false, "If true, any existing principal data in the directory will be overwritten")
cmdFork.Flags.DurationVar(&flagForkFor, "for", 0, "Duration for which the forked blessing is valid (zero means no that the blessing is always valid)")
cmdFork.Flags.StringVar(&flagForkWith, "with", "", "Path to file containing blessing to extend")
cmdBless.Flags.DurationVar(&flagBlessFor, "for", time.Minute, "Duration of blessing validity")
cmdBless.Flags.StringVar(&flagBlessWith, "with", "", "Path to file containing blessing to extend")
cmdBless.Flags.StringVar(&flagBlessRemoteKey, "remote_key", "", "Public key of the remote principal to bless (obtained from the 'recvblessings' command run by the remote principal")
cmdBless.Flags.StringVar(&flagBlessRemoteToken, "remote_token", "", "Token provided by principal running the 'recvblessings' command")
cmdSeekBlessings.Flags.StringVar(&flagSeekBlessingsFrom, "from", "https://auth.dev.v.io:8125/google", "URL to use to begin the seek blessings process")
cmdSeekBlessings.Flags.BoolVar(&flagSeekBlessingsSetDefault, "set_default", true, "If true, the blessings obtained will be set as the default blessing in the store")
cmdSeekBlessings.Flags.StringVar(&flagSeekBlessingsForPeer, "for_peer", string(security.AllPrincipals), "If non-empty, the blessings obtained will be marked for peers matching this pattern in the store")
cmdSeekBlessings.Flags.BoolVar(&flagSeekBlessingsBrowser, "browser", true, "If false, the seekblessings command will not open the browser and only print the url to visit.")
cmdSeekBlessings.Flags.BoolVar(&flagAddToRoots, "add_to_roots", true, "If true, the root certificate of the blessing will be added to the principal's set of recognized root certificates")
cmdStoreSet.Flags.BoolVar(&flagAddToRoots, "add_to_roots", true, "If true, the root certificate of the blessing will be added to the principal's set of recognized root certificates")
cmdStoreSetDefault.Flags.BoolVar(&flagAddToRoots, "add_to_roots", true, "If true, the root certificate of the blessing will be added to the principal's set of recognized root certificates")
cmdCreate.Flags.BoolVar(&flagCreateOverwrite, "overwrite", false, "If true, any existing principal data in the directory will be overwritten")
cmdRecvBlessings.Flags.BoolVar(&flagRecvBlessingsSetDefault, "set_default", true, "If true, the blessings received will be set as the default blessing in the store")
cmdRecvBlessings.Flags.StringVar(&flagRecvBlessingsForPeer, "for_peer", string(security.AllPrincipals), "If non-empty, the blessings received will be marked for peers matching this pattern in the store")
cmdStore := &cmdline.Command{
Name: "store",
Short: "Manipulate and inspect the principal's blessing store",
Long: `
Commands to manipulate and inspect the blessing store of the principal.
All blessings are printed to stdout using base64-VOM-encoding
`,
Children: []*cmdline.Command{cmdStoreDefault, cmdStoreSetDefault, cmdStoreForPeer, cmdStoreSet, cmdStoreAddToRoots},
}
(&cmdline.Command{
Name: "principal",
Short: "Create and manage veyron principals",
Long: `
The principal tool helps create and manage blessings and the set of trusted
roots bound to a principal.
All objects are printed using base64-VOM-encoding.
`,
Children: []*cmdline.Command{cmdCreate, cmdFork, cmdSeekBlessings, cmdRecvBlessings, cmdDump, cmdDumpBlessings, cmdBlessSelf, cmdBless, cmdStore},
}).Main()
}
func decodeBlessings(fname string) (security.Blessings, error) {
var wire security.WireBlessings
if err := decode(fname, &wire); err != nil {
return nil, err
}
return security.NewBlessings(wire)
}
func dumpBlessings(blessings security.Blessings) error {
if blessings == nil {
return errors.New("no blessings found")
}
str, err := base64VomEncode(security.MarshalBlessings(blessings))
if err != nil {
return fmt.Errorf("base64-VOM encoding failed: %v", err)
}
fmt.Println(str)
return nil
}
func read(fname string) (string, error) {
if len(fname) == 0 {
return "", nil
}
f := os.Stdin
if fname != "-" {
var err error
if f, err = os.Open(fname); err != nil {
return "", fmt.Errorf("failed to open %q: %v", fname, err)
}
}
defer f.Close()
var buf bytes.Buffer
if _, err := io.Copy(&buf, f); err != nil {
return "", fmt.Errorf("failed to read %q: %v", fname, err)
}
return buf.String(), nil
}
func decode(fname string, val interface{}) error {
str, err := read(fname)
if err != nil {
return err
}
if err := base64VomDecode(str, val); err != nil || val == nil {
return fmt.Errorf("failed to decode %q: %v", fname, err)
}
return nil
}
func defaultBlessingName() string {
var name string
if user, _ := user.Current(); user != nil && len(user.Username) > 0 {
name = user.Username
} else {
name = "anonymous"
}
if host, _ := os.Hostname(); len(host) > 0 {
name = name + "@" + host
}
return name
}
func rootkey(chain []security.Certificate) string {
if len(chain) == 0 {
return "<empty certificate chain>"
}
key, err := security.UnmarshalPublicKey(chain[0].PublicKey)
if err != nil {
return fmt.Sprintf("<invalid PublicKey: %v>", err)
}
return fmt.Sprintf("%v", key)
}
func base64VomEncode(i interface{}) (string, error) {
buf := &bytes.Buffer{}
closer := base64.NewEncoder(base64.URLEncoding, buf)
if err := vom.NewEncoder(closer).Encode(i); err != nil {
return "", err
}
// Must close the base64 encoder to flush out any partially written
// blocks.
if err := closer.Close(); err != nil {
return "", err
}
return buf.String(), nil
}
func base64VomDecode(s string, i interface{}) error {
b, err := base64.URLEncoding.DecodeString(s)
if err != nil {
return err
}
return vom.NewDecoder(bytes.NewBuffer(b)).Decode(i)
}
type recvBlessingsService struct {
principal security.Principal
notify chan error
token string
}
func (r *recvBlessingsService) Grant(call ipc.ServerCall, token string) error {
b := call.Blessings()
if b == nil {
return fmt.Errorf("no blessings granted by sender")
}
if len(token) != len(r.token) {
// A timing attack can be used to figure out the length
// of the token, but then again, so can looking at the
// source code. So, it's okay.
return fmt.Errorf("blessings received from unexpected sender")
}
if subtle.ConstantTimeCompare([]byte(token), []byte(r.token)) != 1 {
return fmt.Errorf("blessings received from unexpected sender")
}
if flagRecvBlessingsSetDefault {
if err := r.principal.BlessingStore().SetDefault(b); err != nil {
return fmt.Errorf("failed to set blessings %v as default: %v", b, err)
}
}
if pattern := security.BlessingPattern(flagRecvBlessingsForPeer); len(pattern) > 0 {
if _, err := r.principal.BlessingStore().Set(b, pattern); err != nil {
return fmt.Errorf("failed to set blessings %v for peers %v: %v", b, pattern, err)
}
}
if flagAddToRoots {
if err := r.principal.AddToRoots(b); err != nil {
return fmt.Errorf("failed to add blessings to recognized roots: %v", err)
}
}
fmt.Println("Received blessings:", b)
r.notify <- nil
return nil
}
type allowAnyone struct{}
func (allowAnyone) Authorize(security.Context) error { return nil }
type granter struct {
p security.Principal
with security.Blessings
extension string
caveats []security.Caveat
serverKey string
}
func (g *granter) Grant(server security.Blessings) (security.Blessings, error) {
if got := fmt.Sprintf("%v", server.PublicKey()); got != g.serverKey {
// If the granter returns an error, the IPC framework should
// abort the RPC before sending the request to the server.
// Thus, there is no concern about leaking the token to an
// imposter server.
return nil, fmt.Errorf("key mismatch: Remote end has public key %v, want %v", got, g.serverKey)
}
return g.p.Bless(server.PublicKey(), g.with, g.extension, g.caveats[0], g.caveats[1:]...)
}
func (*granter) IPCCallOpt() {}
func sendBlessings(r veyron2.Runtime, object string, granter *granter, remoteToken string) error {
call, err := r.Client().StartCall(r.NewContext(), object, "Grant", []interface{}{remoteToken}, granter)
if err != nil {
return fmt.Errorf("failed to start RPC to %q: %v", object, err)
}
if ierr := call.Finish(&err); ierr != nil {
return fmt.Errorf("failed to finish RPC to %q: %v", object, ierr)
}
return err
}