| // 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. |
| |
| // The following enables go generate to generate the doc.go file. |
| //go:generate go run $JIRI_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go . |
| |
| package main |
| |
| import ( |
| "bytes" |
| "crypto/rand" |
| "crypto/subtle" |
| "encoding/base64" |
| "encoding/json" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "os" |
| "os/user" |
| "strings" |
| "time" |
| |
| "v.io/v23" |
| "v.io/v23/context" |
| "v.io/v23/options" |
| "v.io/v23/rpc" |
| "v.io/v23/security" |
| "v.io/v23/vom" |
| "v.io/x/lib/cmdline" |
| "v.io/x/ref" |
| vsecurity "v.io/x/ref/lib/security" |
| "v.io/x/ref/lib/security/passphrase" |
| "v.io/x/ref/lib/v23cmd" |
| _ "v.io/x/ref/runtime/factories/roaming" |
| ) |
| |
| var ( |
| // Flags for the "blessself" command |
| flagBlessSelfCaveats caveatsFlag |
| flagBlessSelfFor time.Duration |
| |
| // Flags for the "bless" command |
| flagBlessCaveats caveatsFlag |
| flagBlessFor time.Duration |
| flagBlessRequireCaveats bool |
| flagBlessWith string |
| flagBlessRemoteKey string |
| flagBlessRemoteToken string |
| |
| // Flags for the "dump" command |
| flagDumpShort bool |
| |
| // Flags for the "fork" command |
| flagForkCaveats caveatsFlag |
| flagForkFor time.Duration |
| flagForkRequireCaveats bool |
| flagForkWith string |
| |
| // Flags for the "seekblessings" command |
| flagSeekBlessingsFrom string |
| flagSeekBlessingsSetDefault bool |
| flagSeekBlessingsForPeer string |
| flagSeekBlessingsBrowser bool |
| |
| // Flags common to many commands |
| flagAddToRoots bool |
| flagCreateOverwrite bool |
| flagWithPassphrase bool |
| flagRemoteArgFile string |
| |
| // Flags for the "recvblessings" command |
| flagRecvBlessingsSetDefault bool |
| flagRecvBlessingsForPeer string |
| |
| // Flags for the commands that get blessings |
| flagBlessingsNames bool |
| flagBlessingsRootKey string |
| flagBlessingsCaveats string |
| |
| // Flags for the get publickey command. |
| flagGetPublicKeyPretty bool |
| |
| errNoCaveats = fmt.Errorf("no caveats provided: it is generally dangerous to bless another principal without any caveats as that gives them almost unrestricted access to the blesser's credentials. If you really want to do this, set --require-caveats=false") |
| |
| 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. |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| p := v23.GetPrincipal(ctx) |
| def, _ := p.BlessingStore().Default() |
| if flagDumpShort { |
| fmt.Printf("%s\n", printAnnotatedBlessingsNames(def)) |
| return nil |
| } |
| fmt.Printf("Public key : %v\n", p.PublicKey()) |
| // NOTE(caprita): We print the default blessings name |
| // twice (it's also printed as part of the blessing |
| // store below) -- the reason we print it here is to |
| // expose whether the blessings are expired. Ideally, |
| // the blessings store would print the expiry |
| // information about each blessing in the store, but |
| // that would require deeper changes beyond the |
| // principal tool. |
| fmt.Printf("Default Blessings : %s\n", printAnnotatedBlessingsNames(def)) |
| 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. |
| `, |
| Runner: cmdline.RunnerFunc(func(env *cmdline.Env, 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, err := blessings2wire(blessings) |
| if err != nil { |
| return fmt.Errorf("failed to decode certificate chains: %v", err) |
| } |
| fmt.Printf("Blessings : %s\n", printAnnotatedBlessingsNames(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 |
| }), |
| } |
| |
| cmdDumpRoots = &cmdline.Command{ |
| Name: "dumproots", |
| Short: "Dump out blessings of the identity providers of blessings", |
| Long: ` |
| Prints out the blessings of the identity providers of the input blessings. One |
| line per identity provider, each line is a base64url-encoded (RFC 4648, Section |
| 5) vom-encoded Blessings object. |
| `, |
| ArgsName: "<file>", |
| ArgsLong: ` |
| <file> is the path to a file containing blessings (base64url-encoded vom-encoded). |
| - is used for STDIN. |
| `, |
| Runner: cmdline.RunnerFunc(func(env *cmdline.Env, 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 blesseings: %v", err) |
| } |
| for _, root := range security.RootBlessings(blessings) { |
| if err := dumpBlessings(root); err != nil { |
| return err |
| } |
| } |
| 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. Additional |
| caveats can be added with the --caveat 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. |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, 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)) |
| } |
| caveats, err := caveatsFromFlags(flagBlessSelfFor, &flagBlessSelfCaveats) |
| if err != nil { |
| return err |
| } |
| principal := v23.GetPrincipal(ctx) |
| blessing, err := 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. Expiration on the blessing are controlled via the --for flag. |
| Additional caveats are controlled with the --caveat flag. |
| |
| For example, let's say a principal "alice" wants to bless another principal "bob" |
| as "alice:friend", the invocation would be: |
| V23_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. In this case, the blessing is not dumped |
| to STDOUT but sent to the remote end. Use 'principal help recvblessings' for |
| details. |
| |
| When --remote-arg-file is specified, only the blessing extension is required, as all other |
| arguments will be extracted from the specified file. |
| `, |
| 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 the base64url-encoded public |
| key or any other blessings of the 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). |
| OR |
| (d) None (if the --remote-arg-file flag is specified, only <extension> should be provided |
| to bless). |
| |
| <extension> is the string extension that will be applied to create the |
| blessing. |
| |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| if len(flagRemoteArgFile) > 0 { |
| if len(args) > 1 { |
| return fmt.Errorf("when --remote-arg-file is provided, only <extension> is expected, provided %d", len(args)) |
| } |
| if (len(flagBlessRemoteKey) + len(flagBlessRemoteToken)) > 0 { |
| return fmt.Errorf("--remote-key and --remote-token should not be specified when --remote-arg-file is") |
| } |
| } else if len(args) > 2 { |
| return fmt.Errorf("got %d arguments, require at most 2", len(args)) |
| } else if (len(flagBlessRemoteKey) == 0) != (len(flagBlessRemoteToken) == 0) { |
| return fmt.Errorf("either both --remote-key and --remote-token should be set, or neither should") |
| } |
| p := v23.GetPrincipal(ctx) |
| |
| var ( |
| err error |
| with security.Blessings |
| ) |
| 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() |
| } |
| caveats, err := caveatsFromFlags(flagBlessFor, &flagBlessCaveats) |
| if err != nil { |
| return err |
| } |
| if len(caveats) == 0 { |
| if flagBlessRequireCaveats { |
| if err := confirmNoCaveats(env); err != nil { |
| return err |
| } |
| } |
| caveats = []security.Caveat{security.UnconstrainedUse()} |
| } |
| if len(caveats) == 0 { |
| return errNoCaveats |
| } |
| |
| tobless, extension, remoteKey, remoteToken, err := blessArgs(env, args) |
| if err != nil { |
| return err |
| } |
| |
| // Send blessings to a "server" started by a "recvblessings" command, either |
| // with the --remote-arg-file flag, or with --remote-key and --remote-token flags. |
| if len(remoteKey) > 0 { |
| granter := &granter{with, extension, caveats, remoteKey} |
| return blessOverNetwork(ctx, tobless, granter, remoteToken) |
| } |
| |
| // Blessing a principal whose key is available locally. |
| blessings, err := blessOverFileSystem(p, tobless, with, extension, caveats) |
| if err != nil { |
| return err |
| } |
| return dumpBlessings(blessings) |
| }), |
| } |
| |
| cmdGetPublicKey = &cmdline.Command{ |
| Name: "publickey", |
| Short: "Prints the public key of the principal.", |
| Long: ` |
| Prints out the public key of the principal specified by the environment |
| that this tool is running in. |
| |
| The key is printed as a base64url encoded bytes (RFC 4648, Section 5) of the |
| DER-format representation of the key (suitable to be provided as an argument to |
| the 'recognize' command for example). |
| |
| With --pretty, a 16-byte fingerprint of the key instead. This format is easier |
| for humans to read and is used in output of other commands in this program, but |
| is not suitable as an argument to the 'recognize' command. |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| key := v23.GetPrincipal(ctx).PublicKey() |
| if flagGetPublicKeyPretty { |
| fmt.Println(key) |
| return nil |
| } |
| der, err := key.MarshalBinary() |
| if err != nil { |
| return fmt.Errorf("corrupted key: %v", err) |
| } |
| fmt.Println(base64.URLEncoding.EncodeToString(der)) |
| return nil |
| }), |
| } |
| |
| cmdGetTrustedRoots = &cmdline.Command{ |
| Name: "recognizedroots", |
| Short: "Return recognized blessings, and their associated public key.", |
| Long: ` |
| Shows list of blessing names that the principal recognizes, and their associated |
| public key. If the principal is operating as a client, contacted servers must |
| appear on this list. If the principal is operating as a server, clients must |
| present blessings derived from this list. |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| fmt.Printf(v23.GetPrincipal(ctx).Roots().DebugString()) |
| return nil |
| }), |
| } |
| |
| cmdGetPeerMap = &cmdline.Command{ |
| Name: "peermap", |
| Short: "Shows the map from peer pattern to which blessing name to present.", |
| Long: ` |
| Shows the map from peer pattern to which blessing name to present. |
| If the principal operates as a server, it presents its default blessing to all peers. |
| If the principal operates as a client, it presents the map value associated with |
| the peer it contacts. |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| fmt.Printf(v23.GetPrincipal(ctx).BlessingStore().DebugString()) |
| return nil |
| }), |
| } |
| |
| cmdGetForPeer = &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. |
| Providing --names will print the blessings' chain names. |
| Providing --rootkey <chain_name> will print the root key of the certificate chain |
| with chain_name. |
| Providing --caveats <chain_name> will print the caveats on the certificate chain |
| with chain_name. |
| `, |
| 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). |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| return printBlessingsInfo(v23.GetPrincipal(ctx).BlessingStore().ForPeer(args...)) |
| }), |
| } |
| |
| cmdGetDefault = &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. |
| Providing --names will print the default blessings' chain names. |
| Providing --rootkey <chain_name> will print the root key of the certificate chain |
| with chain_name. |
| Providing --caveats <chain_name> will print the caveats on the certificate chain |
| with chain_name. |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| def, _ := v23.GetPrincipal(ctx).BlessingStore().Default() |
| return printBlessingsInfo(def) |
| }), |
| } |
| |
| cmdSetForPeer = &cmdline.Command{ |
| Name: "forpeer", |
| 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 'set forpeer' 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. |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, 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]) |
| |
| p := v23.GetPrincipal(ctx) |
| 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 := security.AddToRoots(p, blessings); err != nil { |
| return fmt.Errorf("AddToRoots failed: %v", err) |
| } |
| } |
| return nil |
| }), |
| } |
| |
| cmdRecognize = &cmdline.Command{ |
| Name: "recognize", |
| Short: "Add to the set of identity providers recognized by this principal", |
| Long: ` |
| Adds an identity provider to the set of recognized root public keys for this principal. |
| |
| It accepts either a single argument (which points to a file containing a blessing) |
| or two arguments (a name and a base64url-encoded DER-encoded public key). |
| |
| For example, to make the principal in credentials directory A recognize the |
| root of the default blessing in credentials directory B: |
| principal -v23.credentials=B bless A some_extension | |
| principal -v23.credentials=A recognize - |
| The extension 'some_extension' has no effect in the command above. |
| |
| Or to make the principal in credentials directory A recognize the public key |
| for the principal in credentials directory B for blessing pattern P: |
| principal -v23.credentials=A recognize P $(principal -v23.credentials=B get publickey) |
| `, |
| ArgsName: "<blessing pattern|blessing> [<key>]", |
| ArgsLong: ` |
| <blessing> is the path to a file containing a blessing typically obtained from |
| this tool. - is used for STDIN. |
| |
| <blessing pattern> is the blessing pattern for which <key> should be recognized. |
| |
| <key> is a base64url-encoded, DER-encoded public key, such as that printed by "principal get publickey". |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| if len(args) != 1 && len(args) != 2 { |
| return fmt.Errorf("requires either one argument <file>, or two arguments <blessing pattern> <key>, provided %d", len(args)) |
| } |
| p := v23.GetPrincipal(ctx) |
| if len(args) == 1 { |
| blessings, err := decodeBlessings(args[0]) |
| if err != nil { |
| return fmt.Errorf("failed to decode provided blessings: %v", err) |
| } |
| if err := security.AddToRoots(p, blessings); err != nil { |
| return fmt.Errorf("AddToRoots failed: %v", err) |
| } |
| return nil |
| } |
| // len(args) == 2 |
| der, err := base64.URLEncoding.DecodeString(args[1]) |
| if err != nil { |
| return fmt.Errorf("invalid base64url encoding of public key: %v", err) |
| } |
| return p.Roots().Add(der, security.BlessingPattern(args[0])) |
| }), |
| } |
| cmdUnion = &cmdline.Command{ |
| Name: "union", |
| Short: "Merge multiple blessings into one", |
| Long: ` |
| Merges multiple blessings into one. |
| |
| It accepts multiple base64url-encoded blessings. Each argument can be a file |
| containing a blessing, or the blessing itself. It returns the union of all the |
| blessings. |
| |
| For example, to merge the blessings contained in files A and B: |
| principal union A B, or |
| principal union $(cat A) $(cat B) |
| `, |
| ArgsName: "[<blessing> | <blessing file>...]", |
| ArgsLong: ` |
| <blessing> is a base64url-encoded blessing. |
| |
| <blessing file> is a file that contains a base64url-encoded blessing. |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| var ret security.Blessings |
| for _, b := range args { |
| encoded := b |
| if b, err := ioutil.ReadFile(b); err == nil { |
| encoded = string(b) |
| } |
| var blessings security.Blessings |
| if err := base64urlVomDecode(encoded, &blessings); err != nil { |
| return err |
| } |
| var err error |
| if ret, err = security.UnionOfBlessings(ret, blessings); err != nil { |
| return err |
| } |
| } |
| out, err := base64urlVomEncode(ret) |
| if err != nil { |
| return err |
| } |
| fmt.Fprintln(env.Stdout, out) |
| return nil |
| }), |
| } |
| |
| cmdSetDefault = &cmdline.Command{ |
| Name: "default", |
| 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 'set default' 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. |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, 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) |
| } |
| |
| p := v23.GetPrincipal(ctx) |
| 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 := security.AddToRoots(p, 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 optional self-blessed blessing and writes |
| it out to the provided directory. The same directory can then be used to set the |
| V23_CREDENTIALS environment variable for other vanadium 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 optional self-blessed blessing that the principal will be |
| setup to use by default. If a blessing argument is not provided, the new |
| principal will have no blessings. |
| `, |
| Runner: cmdline.RunnerFunc(func(env *cmdline.Env, args []string) error { |
| if len(args) < 1 || len(args) > 2 { |
| return fmt.Errorf("requires one or two arguments: <directory> [and optional <blessing>], provided %d", len(args)) |
| } |
| dir := args[0] |
| if flagCreateOverwrite { |
| if err := os.RemoveAll(dir); err != nil { |
| return err |
| } |
| } |
| var pass []byte |
| if flagWithPassphrase { |
| var err error |
| if pass, err = passphrase.Get("Enter passphrase (entering nothing will store the principal key unencrypted): "); err != nil { |
| return err |
| } |
| } |
| p, err := vsecurity.CreatePersistentPrincipal(dir, pass) |
| if err != nil { |
| return err |
| } |
| if len(args) == 2 { |
| name := args[1] |
| 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. Expiration on the blessing |
| are controlled via the --for flag. Additional caveats on the blessing are |
| controlled with the --caveat 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. |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, 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] |
| caveats, err := caveatsFromFlags(flagForkFor, &flagForkCaveats) |
| if err != nil { |
| return err |
| } |
| if !flagForkRequireCaveats && len(caveats) == 0 { |
| caveats = []security.Caveat{security.UnconstrainedUse()} |
| } |
| if len(caveats) == 0 { |
| return errNoCaveats |
| } |
| var with security.Blessings |
| 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, _ = v23.GetPrincipal(ctx).BlessingStore().Default() |
| } |
| |
| if flagCreateOverwrite { |
| if err := os.RemoveAll(dir); err != nil { |
| return err |
| } |
| } |
| var pass []byte |
| if flagWithPassphrase { |
| var err error |
| if pass, err = passphrase.Get("Enter passphrase (entering nothing will store the principal key unencrypted): "); err != nil { |
| return err |
| } |
| } |
| p, err := vsecurity.CreatePersistentPrincipal(dir, pass) |
| if err != nil { |
| return err |
| } |
| |
| key := p.PublicKey() |
| rp := v23.GetPrincipal(ctx) |
| blessings, err := rp.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 Vanadium blessing service", |
| Long: ` |
| Seeks blessings from a web-based Vanadium 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 false, and are also set for sharing with all peers unless a more |
| specific peer pattern is provided using the --for-peer flag. |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| p := v23.GetPrincipal(ctx) |
| |
| blessedChan := make(chan string) |
| defer close(blessedChan) |
| macaroonChan, err := getMacaroonForBlessRPC(p.PublicKey(), flagSeekBlessingsFrom, blessedChan, flagSeekBlessingsBrowser) |
| if err != nil { |
| return fmt.Errorf("failed to get macaroon from Vanadium blesser: %v", err) |
| } |
| |
| blessings, err := exchangeMacaroonForBlessing(ctx, macaroonChan) |
| if err != nil { |
| return err |
| } |
| blessedChan <- fmt.Sprint(blessings) |
| // Wait for getTokenForBlessRPC to clean up: |
| <-macaroonChan |
| |
| if flagSeekBlessingsSetDefault { |
| if err := p.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 := 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 := security.AddToRoots(p, blessings); err != nil { |
| return fmt.Errorf("AddToRoots failed: %v", err) |
| } |
| } |
| fmt.Fprintf(env.Stdout, "Received blessings: %v\n", blessings) |
| return nil |
| }), |
| } |
| |
| 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 false, 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 --v23.proxy flag: |
| principal --v23.proxy=proxy recvblessings |
| |
| The command to be run at the sender is of the form: |
| principal bless --remote-key=KEY --remote-token=TOKEN ADDRESS EXTENSION |
| |
| 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. |
| |
| If the --remote-arg-file flag is provided to recvblessings, the remote key, remote token |
| and object address of this principal will be written to the specified location. |
| This file can be supplied to bless: |
| principal bless --remote-arg-file FILE EXTENSION |
| |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| if len(args) != 0 { |
| return fmt.Errorf("command accepts no arguments") |
| } |
| var token [24]byte |
| if _, err := rand.Read(token[:]); err != nil { |
| return fmt.Errorf("unable to generate token: %v", err) |
| } |
| p := v23.GetPrincipal(ctx) |
| service := &recvBlessingsService{ |
| principal: p, |
| token: base64.URLEncoding.EncodeToString(token[:]), |
| notify: make(chan error), |
| } |
| ctx, server, err := v23.WithNewServer(ctx, "", service, security.AllowEveryone()) |
| if err != nil { |
| return fmt.Errorf("failed to create server to listen for blessings: %v", err) |
| } |
| name := server.Status().Endpoints[0].Name() |
| 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") |
| fmt.Println() |
| if len(flagRemoteArgFile) > 0 { |
| if err := writeRecvBlessingsInfo(flagRemoteArgFile, p.PublicKey().String(), service.token, name); err != nil { |
| return fmt.Errorf("failed to write recvblessings info to %v: %v", flagRemoteArgFile, err) |
| } |
| fmt.Printf("make %q accessible to the blesser, possibly by copying the file over and then run:\n", flagRemoteArgFile) |
| fmt.Printf("principal bless --remote-arg-file=%v", flagRemoteArgFile) |
| } else { |
| fmt.Printf("principal bless --remote-key=%v --remote-token=%v %v\n", p.PublicKey(), service.token, name) |
| } |
| fmt.Println() |
| fmt.Println("...waiting for sender..") |
| return <-service.notify |
| }), |
| } |
| ) |
| |
| func printAnnotatedBlessingsNames(b security.Blessings) string { |
| // If the Blessings are expired, print a message saying so. |
| expiredMessage := "" |
| if exp := b.Expiry(); !exp.IsZero() && exp.Before(time.Now()) { |
| expiredMessage = " [EXPIRED]" |
| } |
| return fmt.Sprintf("%v%s", b, expiredMessage) |
| } |
| |
| func blessArgs(env *cmdline.Env, args []string) (tobless, extension, remoteKey, remoteToken string, err error) { |
| extensionInArgs := false |
| if len(flagRemoteArgFile) == 0 { |
| tobless = args[0] |
| remoteKey = flagBlessRemoteKey |
| remoteToken = flagBlessRemoteToken |
| extensionInArgs = len(args) > 1 |
| } else if len(flagRemoteArgFile) > 0 { |
| remoteKey, remoteToken, tobless, err = blessArgsFromFile(flagRemoteArgFile) |
| extensionInArgs = len(args) > 0 |
| } |
| if extensionInArgs { |
| extension = args[len(args)-1] |
| } else { |
| extension, err = readFromStdin(env, "Extension to use for blessing:") |
| } |
| return |
| } |
| |
| func confirmNoCaveats(env *cmdline.Env) error { |
| text, err := readFromStdin(env, `WARNING: No caveats provided |
| It is generally dangerous to bless another principal without any caveats as |
| that gives them unrestricted access to the blesser's credentials. |
| |
| Caveats can be specified with the --for or --caveat flags. |
| |
| Do you really wish to bless without caveats? (YES to confirm)`) |
| if err != nil || strings.ToUpper(text) != "YES" { |
| return errNoCaveats |
| } |
| return nil |
| } |
| |
| func readFromStdin(env *cmdline.Env, prompt string) (string, error) { |
| fmt.Fprintf(env.Stdout, "%v ", prompt) |
| os.Stdout.Sync() |
| // Cannot use bufio because that may "lose" data beyond the line (the |
| // remainder in the buffer). |
| // Do the inefficient byte-by-byte scan for now - shouldn't be a problem |
| // given the common use case. If that becomes a problem, switch to bufio |
| // and share the bufio.Reader between multiple calls to readFromStdin. |
| buf := make([]byte, 0, 100) |
| r := make([]byte, 1) |
| for { |
| n, err := env.Stdin.Read(r) |
| if n == 1 && r[0] == '\n' { |
| break |
| } |
| if n == 1 { |
| buf = append(buf, r[0]) |
| continue |
| } |
| if err != nil { |
| return "", err |
| } |
| } |
| return strings.TrimSpace(string(buf)), nil |
| } |
| |
| func blessOverFileSystem(p security.Principal, tobless string, with security.Blessings, extension string, caveats []security.Caveat) (security.Blessings, error) { |
| 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 security.Blessings{}, fmt.Errorf("failed to read principal in directory %q: %v", tobless, err) |
| } |
| } |
| key = other.PublicKey() |
| } else if str, err := read(tobless); err != nil { |
| return security.Blessings{}, fmt.Errorf("failed to read %q: %v", tobless, err) |
| } else if b64, err := base64.URLEncoding.DecodeString(str); err != nil { |
| return security.Blessings{}, fmt.Errorf("failed to decode base64url encoded bytes in %q: %v", tobless, err) |
| } else if key, err = security.UnmarshalPublicKey(b64); err != nil { |
| // Not a public key, maybe a blessings object? |
| var b security.Blessings |
| if errb := vom.Decode(b64, &b); errb != nil { |
| return security.Blessings{}, fmt.Errorf("failed to decode blessings (%v) or public key (%v) from %q", errb, err, tobless) |
| } |
| key = b.PublicKey() |
| } |
| return p.Bless(key, with, extension, caveats[0], caveats[1:]...) |
| } |
| |
| type recvBlessingsInfo struct { |
| RemoteKey string `json:"remote_key"` |
| RemoteToken string `json:"remote_token"` |
| Name string `json:"name"` |
| } |
| |
| func writeRecvBlessingsInfo(fname string, remoteKey, remoteToken, name string) error { |
| f, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE, 0666) |
| if err != nil { |
| return err |
| } |
| b, err := json.Marshal(recvBlessingsInfo{remoteKey, remoteToken, name}) |
| if err != nil { |
| return err |
| } |
| if _, err := f.Write(b); err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| func blessArgsFromFile(fname string) (remoteKey, remoteToken, tobless string, err error) { |
| blessJSON, err := ioutil.ReadFile(fname) |
| if err != nil { |
| return "", "", "", err |
| } |
| var binfo recvBlessingsInfo |
| if err := json.Unmarshal(blessJSON, &binfo); err != nil { |
| return "", "", "", err |
| } |
| return binfo.RemoteKey, binfo.RemoteToken, binfo.Name, err |
| } |
| |
| func main() { |
| cmdline.HideGlobalFlagsExcept() |
| cmdBlessSelf.Flags.Var(&flagBlessSelfCaveats, "caveat", flagBlessSelfCaveats.usage()) |
| cmdBlessSelf.Flags.DurationVar(&flagBlessSelfFor, "for", 0, "Duration of blessing validity (zero implies no expiration)") |
| |
| cmdDump.Flags.BoolVar(&flagDumpShort, "s", false, "If true, show only the default blessing names") |
| |
| cmdFork.Flags.BoolVar(&flagCreateOverwrite, "overwrite", false, "If true, any existing principal data in the directory will be overwritten") |
| cmdFork.Flags.BoolVar(&flagWithPassphrase, "with-passphrase", true, "If true, the user is prompted for a passphrase to encrypt the principal. Otherwise, the principal is stored unencrypted.") |
| cmdFork.Flags.Var(&flagForkCaveats, "caveat", flagForkCaveats.usage()) |
| cmdFork.Flags.DurationVar(&flagForkFor, "for", 0, "Duration of blessing validity (zero implies no expiration caveat)") |
| cmdFork.Flags.BoolVar(&flagForkRequireCaveats, "require-caveats", true, "If false, allow blessing without any caveats. This is typically not advised as the principal wielding the blessing will be almost as powerful as its blesser") |
| cmdFork.Flags.StringVar(&flagForkWith, "with", "", "Path to file containing blessing to extend") |
| |
| cmdBless.Flags.Var(&flagBlessCaveats, "caveat", flagBlessCaveats.usage()) |
| cmdBless.Flags.DurationVar(&flagBlessFor, "for", 0, "Duration of blessing validity (zero implies no expiration caveat)") |
| cmdBless.Flags.BoolVar(&flagBlessRequireCaveats, "require-caveats", true, "If false, allow blessing without any caveats. This is typically not advised as the principal wielding the blessing will be almost as powerful as its blesser") |
| 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") |
| cmdBless.Flags.StringVar(&flagRemoteArgFile, "remote-arg-file", "", "File containing bless arguments written by 'principal recvblessings -remote-arg-file FILE EXTENSION' command. This can be provided to bless in place of --remote-key, --remote-token, and <principal>") |
| |
| defaultFrom := "https://dev.v.io/auth/google" |
| if e := os.Getenv(ref.EnvOAuthIdentityProvider); e != "" { |
| defaultFrom = e |
| } |
| cmdSeekBlessings.Flags.StringVar(&flagSeekBlessingsFrom, "from", defaultFrom, "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") |
| |
| cmdSetForPeer.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") |
| |
| cmdSetDefault.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") |
| cmdCreate.Flags.BoolVar(&flagWithPassphrase, "with-passphrase", true, "If true, the user is prompted for a passphrase to encrypt the principal. Otherwise, the principal is stored unencrypted.") |
| |
| 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") |
| cmdRecvBlessings.Flags.StringVar(&flagRemoteArgFile, "remote-arg-file", "", "If non-empty, the remote key, remote token, and principal will be written to the specified file in a JSON object. This can be provided to 'principal bless --remote-arg-file FILE EXTENSION'") |
| |
| cmdGetForPeer.Flags.BoolVar(&flagBlessingsNames, "names", false, "If true, shows the value of the blessing name to be presented to the peer") |
| cmdGetForPeer.Flags.StringVar(&flagBlessingsRootKey, "rootkey", "", "Shows the value of the root key of the provided certificate chain name.") |
| cmdGetForPeer.Flags.StringVar(&flagBlessingsCaveats, "caveats", "", "Shows the caveats on the provided certificate chain name.") |
| |
| cmdGetDefault.Flags.BoolVar(&flagBlessingsNames, "names", false, "If true, shows the value of the blessing name to be presented to the peer") |
| cmdGetDefault.Flags.StringVar(&flagBlessingsRootKey, "rootkey", "", "Shows the value of the root key of the provided certificate chain name.") |
| cmdGetDefault.Flags.StringVar(&flagBlessingsCaveats, "caveats", "", "Shows the caveats on the provided certificate chain name.") |
| |
| cmdGetPublicKey.Flags.BoolVar(&flagGetPublicKeyPretty, "pretty", false, "If true, print the key out in a more human-readable but lossy representation.") |
| |
| cmdSet := &cmdline.Command{ |
| Name: "set", |
| Short: "Mutate the principal's blessings.", |
| Long: ` |
| Commands to mutate the blessings of the principal. |
| |
| All input blessings are expected to be serialized using base64url-vom-encoding. |
| See 'principal get'. |
| `, |
| Children: []*cmdline.Command{cmdSetDefault, cmdSetForPeer}, |
| } |
| |
| cmdGet := &cmdline.Command{ |
| Name: "get", |
| Short: "Read the principal's blessings.", |
| Long: ` |
| Commands to inspect the blessings of the principal. |
| |
| All blessings are printed to stdout using base64url-vom-encoding. |
| `, |
| Children: []*cmdline.Command{cmdGetDefault, cmdGetForPeer, cmdGetPublicKey, cmdGetTrustedRoots, cmdGetPeerMap}, |
| } |
| |
| root := &cmdline.Command{ |
| Name: "principal", |
| Short: "creates and manages Vanadium principals and blessings", |
| Long: ` |
| Command principal creates and manages Vanadium principals and blessings. |
| |
| All objects are printed using base64url-vom-encoding. |
| `, |
| Children: []*cmdline.Command{cmdCreate, cmdFork, cmdSeekBlessings, cmdRecvBlessings, cmdDump, cmdDumpBlessings, cmdDumpRoots, cmdBlessSelf, cmdBless, cmdSet, cmdGet, cmdRecognize, cmdUnion}, |
| } |
| cmdline.Main(root) |
| } |
| |
| func decodeBlessings(fname string) (security.Blessings, error) { |
| var b security.Blessings |
| err := decode(fname, &b) |
| return b, err |
| } |
| |
| func dumpBlessings(blessings security.Blessings) error { |
| if blessings.IsZero() { |
| return fmt.Errorf("no blessings found") |
| } |
| str, err := base64urlVomEncode(blessings) |
| if err != nil { |
| return fmt.Errorf("base64url-vom encoding failed: %v", err) |
| } |
| fmt.Println(str) |
| return nil |
| } |
| |
| func printBlessingsInfo(blessings security.Blessings) error { |
| if blessings.IsZero() { |
| return fmt.Errorf("no blessings found") |
| } |
| if flagBlessingsNames { |
| fmt.Println(strings.Replace(fmt.Sprint(blessings), ",", "\n", -1)) |
| return nil |
| } else if len(flagBlessingsRootKey) > 0 { |
| chain, err := getChainByName(blessings, flagBlessingsRootKey) |
| if err != nil { |
| return err |
| } |
| fmt.Println(rootkey(chain)) |
| return nil |
| } else if len(flagBlessingsCaveats) > 0 { |
| chain, err := getChainByName(blessings, flagBlessingsCaveats) |
| if err != nil { |
| return err |
| } |
| cavs, err := prettyPrintCaveats(chain) |
| if err != nil { |
| return err |
| } |
| for _, c := range cavs { |
| fmt.Println(c) |
| } |
| return nil |
| } |
| return dumpBlessings(blessings) |
| } |
| |
| func prettyPrintCaveats(chain []security.Certificate) ([]string, error) { |
| var cavs []security.Caveat |
| for _, cert := range chain { |
| cavs = append(cavs, cert.Caveats...) |
| } |
| var s []string |
| for _, cav := range cavs { |
| if cav.Id == security.PublicKeyThirdPartyCaveat.Id { |
| c := cav.ThirdPartyDetails() |
| s = append(s, fmt.Sprintf("ThirdPartyCaveat: Requires discharge from %v (ID=%q)", c.Location(), c.ID())) |
| continue |
| } |
| var param interface{} |
| if err := vom.Decode(cav.ParamVom, ¶m); err != nil { |
| return nil, err |
| } |
| switch cav.Id { |
| case security.ConstCaveat.Id: |
| // In the case a ConstCaveat is specified, we only want to print it |
| // if it never validates. |
| if !param.(bool) { |
| s = append(s, fmt.Sprintf("Never validates")) |
| } |
| case security.ExpiryCaveat.Id: |
| s = append(s, fmt.Sprintf("Expires at %v", param)) |
| case security.MethodCaveat.Id: |
| s = append(s, fmt.Sprintf("Restricted to methods %v", param)) |
| case security.PeerBlessingsCaveat.Id: |
| s = append(s, fmt.Sprintf("Restricted to peers with blessings %v", param)) |
| default: |
| s = append(s, cav.String()) |
| } |
| } |
| return s, nil |
| } |
| |
| func getChainByName(b security.Blessings, name string) ([]security.Certificate, error) { |
| wire, err := blessings2wire(b) |
| if err != nil { |
| return nil, err |
| } |
| for _, chain := range wire.CertificateChains { |
| if chainName(chain) == name { |
| return chain, nil |
| } |
| } |
| return nil, fmt.Errorf("no chains of name %v in %v", name, b) |
| } |
| |
| 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 := base64urlVomDecode(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 chainName(chain []security.Certificate) string { |
| exts := make([]string, len(chain)) |
| for i, cert := range chain { |
| exts[i] = cert.Extension |
| } |
| return strings.Join(exts, security.ChainSeparator) |
| } |
| |
| func base64urlVomEncode(i interface{}) (string, error) { |
| buf := &bytes.Buffer{} |
| closer := base64.NewEncoder(base64.URLEncoding, buf) |
| enc := vom.NewEncoder(closer) |
| if err := enc.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 base64urlVomDecode(s string, i interface{}) error { |
| b, err := base64.URLEncoding.DecodeString(s) |
| if err != nil { |
| return err |
| } |
| dec := vom.NewDecoder(bytes.NewBuffer(b)) |
| return dec.Decode(i) |
| } |
| |
| type recvBlessingsService struct { |
| principal security.Principal |
| notify chan error |
| token string |
| } |
| |
| func (r *recvBlessingsService) Grant(_ *context.T, call rpc.ServerCall, token string) error { |
| b := call.GrantedBlessings() |
| if b.IsZero() { |
| 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 := security.AddToRoots(r.principal, 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 granter struct { |
| with security.Blessings |
| extension string |
| caveats []security.Caveat |
| serverKey string |
| } |
| |
| func (g *granter) Grant(ctx *context.T, call security.Call) (security.Blessings, error) { |
| server := call.RemoteBlessings() |
| p := call.LocalPrincipal() |
| if got := fmt.Sprintf("%v", server.PublicKey()); got != g.serverKey { |
| // If the granter returns an error, the RPC 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 security.Blessings{}, fmt.Errorf("key mismatch: Remote end has public key %v, want %v", got, g.serverKey) |
| } |
| return p.Bless(server.PublicKey(), g.with, g.extension, g.caveats[0], g.caveats[1:]...) |
| } |
| func (*granter) RPCCallOpt() {} |
| |
| func blessOverNetwork(ctx *context.T, object string, granter *granter, remoteToken string) error { |
| client := v23.GetClient(ctx) |
| // The receiver is being authorized based on the hash of its public key |
| // (see Grant), so it should be fine to ignore the blessing names in the endpoint |
| // (which are likely to not be recognized by the sender anyway). |
| // |
| // At worst, there is a privacy leak of the senders intent to send some |
| // blessings. That could be addressed by making the full public key of |
| // the recipeint available to the sender and using |
| // options.SecurityAuthorizer{security.PublicKeyAuthorizer()} instead |
| // of providing a "hash" of the recipients public key and verifying in |
| // the Granter implementation. |
| if err := client.Call( |
| ctx, |
| object, |
| "Grant", |
| []interface{}{remoteToken}, |
| nil, |
| granter, |
| options.ServerAuthorizer{security.AllowEveryone()}, |
| options.NameResolutionAuthorizer{security.AllowEveryone()}); err != nil { |
| return fmt.Errorf("failed to make RPC to %q: %v", object, err) |
| } |
| return nil |
| } |
| |
| func caveatsFromFlags(expiry time.Duration, caveatsFlag *caveatsFlag) ([]security.Caveat, error) { |
| caveats, err := caveatsFlag.Compile() |
| if err != nil { |
| return nil, fmt.Errorf("failed to parse caveats: %v", err) |
| } |
| if expiry != 0 { |
| ecav, err := security.NewExpiryCaveat(time.Now().Add(expiry)) |
| if err != nil { |
| return nil, fmt.Errorf("failed to create expiration caveat: %v", err) |
| } |
| caveats = append(caveats, ecav) |
| } |
| return caveats, nil |
| } |
| |
| // Circuitous route to get to the certificate chains. |
| // See comments on why security.MarshalBlessings is discouraged. |
| // Though, a better alternative is worth looking into. |
| func blessings2wire(b security.Blessings) (security.WireBlessings, error) { |
| var wire security.WireBlessings |
| data, err := vom.Encode(b) |
| if err != nil { |
| return wire, err |
| } |
| err = vom.Decode(data, &wire) |
| return wire, err |
| } |