blob: 67fb4e14b92ca4a19b91d189591f269a3884132d [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -08005// The following enables go generate to generate the doc.go file.
Jiri Simsa24a71552015-02-27 11:31:36 -08006//go:generate go run $VANADIUM_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -08007
Ankurcf6a89f2014-10-06 18:33:03 -07008package main
9
10import (
11 "bytes"
Asim Shankarf11b1bc2014-11-12 17:18:45 -080012 "crypto/rand"
13 "crypto/subtle"
Asim Shankarb3a82ba2014-10-29 11:41:27 -070014 "encoding/base64"
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -070015 "encoding/json"
Ankurcf6a89f2014-10-06 18:33:03 -070016 "fmt"
17 "io"
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -070018 "io/ioutil"
Ankurcf6a89f2014-10-06 18:33:03 -070019 "os"
20 "os/user"
21 "time"
22
Jiri Simsa6ac95222015-02-23 16:11:49 -080023 "v.io/v23"
24 "v.io/v23/context"
Asim Shankar263c73b2015-03-19 18:31:26 -070025 "v.io/v23/options"
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070026 "v.io/v23/rpc"
Jiri Simsa6ac95222015-02-23 16:11:49 -080027 "v.io/v23/security"
28 "v.io/v23/vom"
Jiri Simsa24a71552015-02-27 11:31:36 -080029 "v.io/x/lib/cmdline"
Jiri Simsaffceefa2015-02-28 11:03:34 -080030 _ "v.io/x/ref/profiles/static"
31 vsecurity "v.io/x/ref/security"
Ankurcf6a89f2014-10-06 18:33:03 -070032)
33
34var (
35 // Flags for the "blessself" command
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -080036 flagBlessSelfCaveats caveatsFlag
37 flagBlessSelfFor time.Duration
Ankurcf6a89f2014-10-06 18:33:03 -070038
Asim Shankar66c52f92014-10-15 23:39:10 -070039 // Flags for the "bless" command
Asim Shankara0bba462015-02-20 22:50:51 -080040 flagBlessCaveats caveatsFlag
41 flagBlessFor time.Duration
42 flagBlessRequireCaveats bool
43 flagBlessWith string
44 flagBlessRemoteKey string
45 flagBlessRemoteToken string
Asim Shankar66c52f92014-10-15 23:39:10 -070046
Ankur77c32ac2014-12-18 14:18:19 -080047 // Flags for the "fork" command
Asim Shankara0bba462015-02-20 22:50:51 -080048 flagForkCaveats caveatsFlag
49 flagForkFor time.Duration
50 flagForkRequireCaveats bool
51 flagForkWith string
Ankur77c32ac2014-12-18 14:18:19 -080052
Asim Shankar66c52f92014-10-15 23:39:10 -070053 // Flags for the "seekblessings" command
54 flagSeekBlessingsFrom string
55 flagSeekBlessingsSetDefault bool
56 flagSeekBlessingsForPeer string
Suharsh Sivakumara76dba62014-12-22 16:00:34 -080057 flagSeekBlessingsBrowser bool
Asim Shankar66c52f92014-10-15 23:39:10 -070058
59 // Flags common to many commands
Ankurc24ff422014-12-16 17:59:26 -080060 flagAddToRoots bool
61 flagCreateOverwrite bool
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -070062 flagRemoteArgFile string
Ankur1d46f552014-10-09 12:13:31 -070063
Ankure548f392014-12-08 18:42:41 -080064 // Flags for the "recvblessings" command
65 flagRecvBlessingsSetDefault bool
66 flagRecvBlessingsForPeer string
67
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -070068 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")
Asim Shankara0bba462015-02-20 22:50:51 -080069 cmdDump = &cmdline.Command{
Ankur1615a7d2014-10-09 11:58:02 -070070 Name: "dump",
71 Short: "Dump out information about the principal",
72 Long: `
Asim Shankar66c52f92014-10-15 23:39:10 -070073Prints out information about the principal specified by the environment
Asim Shankar1789b8a2014-10-31 17:31:41 -070074that this tool is running in.
Ankur1615a7d2014-10-09 11:58:02 -070075`,
76 Run: func(cmd *cmdline.Command, args []string) error {
Jiri Simsa6ac95222015-02-23 16:11:49 -080077 ctx, shutdown := v23.Init()
Matt Rosencrantza5ad2722015-01-22 11:17:47 -080078 defer shutdown()
Matt Rosencrantz574d5e12014-11-26 10:01:37 -080079
Jiri Simsa6ac95222015-02-23 16:11:49 -080080 p := v23.GetPrincipal(ctx)
Ankur1615a7d2014-10-09 11:58:02 -070081 fmt.Printf("Public key : %v\n", p.PublicKey())
Ankur1615a7d2014-10-09 11:58:02 -070082 fmt.Println("---------------- BlessingStore ----------------")
83 fmt.Printf("%v", p.BlessingStore().DebugString())
Ankur1615a7d2014-10-09 11:58:02 -070084 fmt.Println("---------------- BlessingRoots ----------------")
85 fmt.Printf("%v", p.Roots().DebugString())
86 return nil
87 },
88 }
89
Asim Shankar66c52f92014-10-15 23:39:10 -070090 cmdDumpBlessings = &cmdline.Command{
91 Name: "dumpblessings",
92 Short: "Dump out information about the provided blessings",
Ankurcf6a89f2014-10-06 18:33:03 -070093 Long: `
Asim Shankar66c52f92014-10-15 23:39:10 -070094Prints out information about the blessings (typically obtained from this tool)
Ankurcf6a89f2014-10-06 18:33:03 -070095encoded in the provided file.
96`,
97 ArgsName: "<file>",
98 ArgsLong: `
Asim Shankar66c52f92014-10-15 23:39:10 -070099<file> is the path to a file containing blessings typically obtained from
Ankurcf6a89f2014-10-06 18:33:03 -0700100this tool. - is used for STDIN.
101`,
102 Run: func(cmd *cmdline.Command, args []string) error {
103 if len(args) != 1 {
104 return fmt.Errorf("requires exactly one argument, <file>, provided %d", len(args))
105 }
106 blessings, err := decodeBlessings(args[0])
107 if err != nil {
108 return fmt.Errorf("failed to decode provided blessings: %v", err)
109 }
Asim Shankarb07ec692015-02-27 23:40:44 -0800110 wire, err := blessings2wire(blessings)
111 if err != nil {
112 return fmt.Errorf("failed to decode certificate chains: %v", err)
113 }
Asim Shankar66c52f92014-10-15 23:39:10 -0700114 fmt.Printf("Blessings : %v\n", blessings)
115 fmt.Printf("PublicKey : %v\n", blessings.PublicKey())
Asim Shankardf88a2e2014-10-21 17:20:28 -0700116 fmt.Printf("Certificate chains : %d\n", len(wire.CertificateChains))
Asim Shankar66c52f92014-10-15 23:39:10 -0700117 for idx, chain := range wire.CertificateChains {
Asim Shankardf88a2e2014-10-21 17:20:28 -0700118 fmt.Printf("Chain #%d (%d certificates). Root certificate public key: %v\n", idx, len(chain), rootkey(chain))
119 for certidx, cert := range chain {
120 fmt.Printf(" Certificate #%d: %v with ", certidx, cert.Extension)
121 switch n := len(cert.Caveats); n {
122 case 1:
123 fmt.Printf("1 caveat")
124 default:
125 fmt.Printf("%d caveats", n)
126 }
127 fmt.Println("")
128 for cavidx, cav := range cert.Caveats {
129 fmt.Printf(" (%d) %v\n", cavidx, &cav)
130 }
Asim Shankar66c52f92014-10-15 23:39:10 -0700131 }
Asim Shankar66c52f92014-10-15 23:39:10 -0700132 }
Ankurcf6a89f2014-10-06 18:33:03 -0700133 return nil
134 },
135 }
136
137 cmdBlessSelf = &cmdline.Command{
138 Name: "blessself",
139 Short: "Generate a self-signed blessing",
140 Long: `
Asim Shankar1789b8a2014-10-31 17:31:41 -0700141Returns a blessing with name <name> and self-signed by the principal specified
142by the environment that this tool is running in. Optionally, the blessing can
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -0800143be restricted with an expiry caveat specified using the --for flag. Additional
144caveats can be added with the --caveat flag.
Ankurcf6a89f2014-10-06 18:33:03 -0700145`,
146 ArgsName: "[<name>]",
147 ArgsLong: `
148<name> is the name used to create the self-signed blessing. If not
149specified, a name will be generated based on the hostname of the
150machine and the name of the user running this command.
151`,
152 Run: func(cmd *cmdline.Command, args []string) error {
Ankurcf6a89f2014-10-06 18:33:03 -0700153 var name string
154 switch len(args) {
155 case 0:
156 name = defaultBlessingName()
157 case 1:
158 name = args[0]
159 default:
160 return fmt.Errorf("requires at most one argument, provided %d", len(args))
161 }
162
Asim Shankara0bba462015-02-20 22:50:51 -0800163 caveats, err := caveatsFromFlags(flagBlessSelfFor, &flagBlessSelfCaveats)
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -0800164 if err != nil {
Asim Shankara0bba462015-02-20 22:50:51 -0800165 return err
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -0800166 }
Jiri Simsa6ac95222015-02-23 16:11:49 -0800167 ctx, shutdown := v23.Init()
Matt Rosencrantza5ad2722015-01-22 11:17:47 -0800168 defer shutdown()
Jiri Simsa6ac95222015-02-23 16:11:49 -0800169 principal := v23.GetPrincipal(ctx)
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -0800170 blessing, err := principal.BlessSelf(name, caveats...)
Ankurcf6a89f2014-10-06 18:33:03 -0700171 if err != nil {
172 return fmt.Errorf("failed to create self-signed blessing for name %q: %v", name, err)
173 }
174
175 return dumpBlessings(blessing)
176 },
177 }
178
Asim Shankar66c52f92014-10-15 23:39:10 -0700179 cmdBless = &cmdline.Command{
180 Name: "bless",
181 Short: "Bless another principal",
182 Long: `
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800183Bless another principal.
Asim Shankar66c52f92014-10-15 23:39:10 -0700184
Ankurc24ff422014-12-16 17:59:26 -0800185The blesser is obtained from the runtime this tool is using. The blessing that
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800186will be extended is the default one from the blesser's store, or specified by
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -0800187the --with flag. Expiration on the blessing are controlled via the --for flag.
188Additional caveats are controlled with the --caveat flag.
Asim Shankar66c52f92014-10-15 23:39:10 -0700189
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800190For example, let's say a principal "alice" wants to bless another principal "bob"
191as "alice/friend", the invocation would be:
Asim Shankar59b8b692015-03-30 01:23:36 -0700192 V23_CREDENTIALS=<path to alice> principal bless <path to bob> friend
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800193and this will dump the blessing to STDOUT.
194
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700195With the --remote-key and --remote-token flags, this command can be used to
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800196bless a principal on a remote machine as well. In this case, the blessing is
197not dumped to STDOUT but sent to the remote end. Use 'principal help
198recvblessings' for more details on that.
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700199
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700200When --remote-arg-file is specified, only the blessing extension is required, as all other
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700201arguments will be extracted from the specified file.
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800202`,
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700203 ArgsName: "[<principal to bless>] <extension>",
Asim Shankar66c52f92014-10-15 23:39:10 -0700204 ArgsLong: `
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800205<principal to bless> represents the principal to be blessed (i.e., whose public
206key will be provided with a name). This can be either:
207(a) The directory containing credentials for that principal,
208OR
209(b) The filename (- for STDIN) containing any other blessings of that
210 principal,
211OR
212(c) The object name produced by the 'recvblessings' command of this tool
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700213 running on behalf of another principal (if the --remote-key and
214 --remote-token flags are specified).
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700215OR
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700216(d) None (if the --remote-arg-file flag is specified, only <extension> should be provided
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700217 to bless).
Asim Shankar66c52f92014-10-15 23:39:10 -0700218
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800219<extension> is the string extension that will be applied to create the
220blessing.
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700221
Asim Shankar66c52f92014-10-15 23:39:10 -0700222 `,
223 Run: func(cmd *cmdline.Command, args []string) error {
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700224 if len(flagRemoteArgFile) > 0 && len(args) != 1 {
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700225 return fmt.Errorf("when --remote-arg-file is provided, only <extension> is expected, provided %d", len(args))
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700226 } else if len(flagRemoteArgFile) == 0 && len(args) != 2 {
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700227 return fmt.Errorf("require exactly two arguments when --remote-arg-file is not provided, provided %d", len(args))
Asim Shankar66c52f92014-10-15 23:39:10 -0700228 }
Matt Rosencrantz574d5e12014-11-26 10:01:37 -0800229
Jiri Simsa6ac95222015-02-23 16:11:49 -0800230 ctx, shutdown := v23.Init()
Matt Rosencrantza5ad2722015-01-22 11:17:47 -0800231 defer shutdown()
Matt Rosencrantz574d5e12014-11-26 10:01:37 -0800232
Jiri Simsa6ac95222015-02-23 16:11:49 -0800233 p := v23.GetPrincipal(ctx)
Asim Shankar66c52f92014-10-15 23:39:10 -0700234
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -0800235 var (
236 err error
237 with security.Blessings
238 )
Asim Shankar66c52f92014-10-15 23:39:10 -0700239 if len(flagBlessWith) > 0 {
240 if with, err = decodeBlessings(flagBlessWith); err != nil {
241 return fmt.Errorf("failed to read blessings from --with=%q: %v", flagBlessWith, err)
242 }
243 } else {
244 with = p.BlessingStore().Default()
245 }
Asim Shankara0bba462015-02-20 22:50:51 -0800246 caveats, err := caveatsFromFlags(flagBlessFor, &flagBlessCaveats)
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -0800247 if err != nil {
Asim Shankara0bba462015-02-20 22:50:51 -0800248 return err
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -0800249 }
Asim Shankara0bba462015-02-20 22:50:51 -0800250 if !flagBlessRequireCaveats && len(caveats) == 0 {
251 caveats = []security.Caveat{security.UnconstrainedUse()}
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800252 }
Bogdan Caprita553e5822015-02-20 14:50:41 -0800253 if len(caveats) == 0 {
Asim Shankara0bba462015-02-20 22:50:51 -0800254 return errNoCaveats
Bogdan Caprita553e5822015-02-20 14:50:41 -0800255 }
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700256
257 tobless, extension, remoteKey, remoteToken, err := blessArgs(args)
Asim Shankar66c52f92014-10-15 23:39:10 -0700258 if err != nil {
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700259 return err
260 }
261
262 // Send blessings to a "server" started by a "recvblessings" command, either
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700263 // with the --remote-arg-file flag, or with --remote-key and --remote-token flags.
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700264 if len(remoteKey) > 0 {
265 granter := &granter{p, with, extension, caveats, remoteKey}
266 return blessOverNetwork(ctx, tobless, granter, remoteToken)
267 }
268
269 // Blessing a principal whose key is available locally.
270 blessings, err := blessOverFileSystem(p, tobless, with, extension, caveats)
271 if err != nil {
272 return err
Asim Shankar66c52f92014-10-15 23:39:10 -0700273 }
274 return dumpBlessings(blessings)
275 },
276 }
277
Suharsh Sivakumar32c8e752015-03-31 19:26:28 -0700278 cmdGetForPeer = &cmdline.Command{
Asim Shankar66c52f92014-10-15 23:39:10 -0700279 Name: "forpeer",
Ankurcf6a89f2014-10-06 18:33:03 -0700280 Short: "Return blessings marked for the provided peer",
281 Long: `
282Returns blessings that are marked for the provided peer in the
Asim Shankar1789b8a2014-10-31 17:31:41 -0700283BlessingStore specified by the environment that this tool is
284running in.
Ankurcf6a89f2014-10-06 18:33:03 -0700285`,
286 ArgsName: "[<peer_1> ... <peer_k>]",
287 ArgsLong: `
288<peer_1> ... <peer_k> are the (human-readable string) blessings bound
289to the peer. The returned blessings are marked with a pattern that is
290matched by at least one of these. If no arguments are specified,
291store.forpeer returns the blessings that are marked for all peers (i.e.,
292blessings set on the store with the "..." pattern).
293`,
294 Run: func(cmd *cmdline.Command, args []string) error {
Jiri Simsa6ac95222015-02-23 16:11:49 -0800295 ctx, shutdown := v23.Init()
Matt Rosencrantza5ad2722015-01-22 11:17:47 -0800296 defer shutdown()
Jiri Simsa6ac95222015-02-23 16:11:49 -0800297 principal := v23.GetPrincipal(ctx)
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -0800298 return dumpBlessings(principal.BlessingStore().ForPeer(args...))
Ankurcf6a89f2014-10-06 18:33:03 -0700299 },
300 }
301
Suharsh Sivakumar32c8e752015-03-31 19:26:28 -0700302 cmdGetDefault = &cmdline.Command{
Asim Shankar66c52f92014-10-15 23:39:10 -0700303 Name: "default",
Ankurcf6a89f2014-10-06 18:33:03 -0700304 Short: "Return blessings marked as default",
305 Long: `
Asim Shankar1789b8a2014-10-31 17:31:41 -0700306Returns blessings that are marked as default in the BlessingStore specified by
307the environment that this tool is running in.
Ankurcf6a89f2014-10-06 18:33:03 -0700308`,
309 Run: func(cmd *cmdline.Command, args []string) error {
Jiri Simsa6ac95222015-02-23 16:11:49 -0800310 ctx, shutdown := v23.Init()
Matt Rosencrantza5ad2722015-01-22 11:17:47 -0800311 defer shutdown()
Jiri Simsa6ac95222015-02-23 16:11:49 -0800312 principal := v23.GetPrincipal(ctx)
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -0800313 return dumpBlessings(principal.BlessingStore().Default())
Ankurcf6a89f2014-10-06 18:33:03 -0700314 },
315 }
Ankur8d304122014-10-07 10:43:43 -0700316
Suharsh Sivakumar32c8e752015-03-31 19:26:28 -0700317 cmdSetForPeer = &cmdline.Command{
318 Name: "forpeer",
Ankur8d304122014-10-07 10:43:43 -0700319 Short: "Set provided blessings for peer",
320 Long: `
Asim Shankar1789b8a2014-10-31 17:31:41 -0700321Marks the provided blessings to be shared with the provided peers on the
322BlessingStore specified by the environment that this tool is running in.
Ankur8d304122014-10-07 10:43:43 -0700323
324'set b pattern' marks the intention to reveal b to peers who
325present blessings of their own matching 'pattern'.
326
327'set nil pattern' can be used to remove the blessings previously
328associated with the pattern (by a prior 'set' command).
329
Suharsh Sivakumar32c8e752015-03-31 19:26:28 -0700330It is an error to call 'set forpeer' with blessings whose public
Ankur8d304122014-10-07 10:43:43 -0700331key does not match the public key of this principal specified
332by the environment.
333`,
334 ArgsName: "<file> <pattern>",
335 ArgsLong: `
336<file> is the path to a file containing a blessing typically obtained
337from this tool. - is used for STDIN.
338
339<pattern> is the BlessingPattern used to identify peers with whom this
340blessing can be shared with.
341`,
342 Run: func(cmd *cmdline.Command, args []string) error {
343 if len(args) != 2 {
Asim Shankar66c52f92014-10-15 23:39:10 -0700344 return fmt.Errorf("requires exactly two arguments <file>, <pattern>, provided %d", len(args))
Ankur8d304122014-10-07 10:43:43 -0700345 }
346 blessings, err := decodeBlessings(args[0])
347 if err != nil {
348 return fmt.Errorf("failed to decode provided blessings: %v", err)
349 }
350 pattern := security.BlessingPattern(args[1])
Matt Rosencrantz574d5e12014-11-26 10:01:37 -0800351
Jiri Simsa6ac95222015-02-23 16:11:49 -0800352 ctx, shutdown := v23.Init()
Matt Rosencrantza5ad2722015-01-22 11:17:47 -0800353 defer shutdown()
Matt Rosencrantz574d5e12014-11-26 10:01:37 -0800354
Jiri Simsa6ac95222015-02-23 16:11:49 -0800355 p := v23.GetPrincipal(ctx)
Asim Shankar66c52f92014-10-15 23:39:10 -0700356 if _, err := p.BlessingStore().Set(blessings, pattern); err != nil {
Ankur8d304122014-10-07 10:43:43 -0700357 return fmt.Errorf("failed to set blessings %v for peers %v: %v", blessings, pattern, err)
358 }
Asim Shankar66c52f92014-10-15 23:39:10 -0700359 if flagAddToRoots {
360 if err := p.AddToRoots(blessings); err != nil {
361 return fmt.Errorf("AddToRoots failed: %v", err)
362 }
363 }
Ankur8d304122014-10-07 10:43:43 -0700364 return nil
365 },
366 }
367
Asim Shankar5fbe3262015-03-11 23:03:51 -0700368 cmdAddToRoots = &cmdline.Command{
Mike Burrows367515e2014-12-02 11:30:18 -0800369 Name: "addtoroots",
Asim Shankar80277d02015-03-31 12:57:06 -0700370 Short: "Add to the set of identity providers recognized by this principal",
Mike Burrows367515e2014-12-02 11:30:18 -0800371 Long: `
Asim Shankar80277d02015-03-31 12:57:06 -0700372Adds an identity provider to the set of recognized roots public keys for this principal.
Mike Burrows367515e2014-12-02 11:30:18 -0800373
Asim Shankar80277d02015-03-31 12:57:06 -0700374It accepts either a single argument (which points to a file containing a blessing)
375or two arguments (a name and a base64-encoded DER-encoded public key).
Mike Burrows367515e2014-12-02 11:30:18 -0800376
Asim Shankar80277d02015-03-31 12:57:06 -0700377For example, to make the principal in credentials directory A recognize the
Mike Burrows367515e2014-12-02 11:30:18 -0800378root of the default blessing in credentials directory B:
Asim Shankarf32d24d2015-04-01 16:34:26 -0700379 principal -v23.credentials=B bless A some_extension |
380 principal -v23.credentials=A addtoroots -
Mike Burrows367515e2014-12-02 11:30:18 -0800381The extension 'some_extension' has no effect in the command above.
Asim Shankar80277d02015-03-31 12:57:06 -0700382
383Or to make the principal in credentials director A recognize the base64-encoded
384public key KEY for blessing patterns P:
Asim Shankarf32d24d2015-04-01 16:34:26 -0700385 principal -v23.credentials=A addtoroots KEY P
Mike Burrows367515e2014-12-02 11:30:18 -0800386`,
Asim Shankar80277d02015-03-31 12:57:06 -0700387 ArgsName: "<key|blessing> [<blessing pattern>]",
Mike Burrows367515e2014-12-02 11:30:18 -0800388 ArgsLong: `
Asim Shankar80277d02015-03-31 12:57:06 -0700389<blessing> is the path to a file containing a blessing typically obtained from
390this tool. - is used for STDIN.
391
392<key> is a base64-encoded, DER-encoded public key.
393
394<blessing pattern> is the blessing pattern for which <key> should be recognized.
Mike Burrows367515e2014-12-02 11:30:18 -0800395`,
396 Run: func(cmd *cmdline.Command, args []string) error {
Asim Shankar80277d02015-03-31 12:57:06 -0700397 if len(args) != 1 && len(args) != 2 {
398 return fmt.Errorf("requires either one argument <file>, or two arguments <key> <blessing pattern>, provided %d", len(args))
Mike Burrows367515e2014-12-02 11:30:18 -0800399 }
Jiri Simsa6ac95222015-02-23 16:11:49 -0800400 ctx, shutdown := v23.Init()
Matt Rosencrantza5ad2722015-01-22 11:17:47 -0800401 defer shutdown()
Mike Burrows367515e2014-12-02 11:30:18 -0800402
Jiri Simsa6ac95222015-02-23 16:11:49 -0800403 p := v23.GetPrincipal(ctx)
Asim Shankar80277d02015-03-31 12:57:06 -0700404 if len(args) == 1 {
405 blessings, err := decodeBlessings(args[0])
406 if err != nil {
407 return fmt.Errorf("failed to decode provided blessings: %v", err)
408 }
409 if err := p.AddToRoots(blessings); err != nil {
410 return fmt.Errorf("AddToRoots failed: %v", err)
411 }
412 return nil
Mike Burrows367515e2014-12-02 11:30:18 -0800413 }
Asim Shankar80277d02015-03-31 12:57:06 -0700414 // len(args) == 2
415 der, err := base64.URLEncoding.DecodeString(args[0])
416 if err != nil {
417 return fmt.Errorf("invalid base64 encoding of public key: %v", err)
418 }
419 key, err := security.UnmarshalPublicKey(der)
420 if err != nil {
421 return fmt.Errorf("invalid DER encoding of public key: %v", err)
422 }
423 return p.Roots().Add(key, security.BlessingPattern(args[1]))
Mike Burrows367515e2014-12-02 11:30:18 -0800424 },
425 }
426
Suharsh Sivakumar32c8e752015-03-31 19:26:28 -0700427 cmdSetDefault = &cmdline.Command{
428 Name: "default",
Ankur8d304122014-10-07 10:43:43 -0700429 Short: "Set provided blessings as default",
430 Long: `
Asim Shankar1789b8a2014-10-31 17:31:41 -0700431Sets the provided blessings as default in the BlessingStore specified by the
432environment that this tool is running in.
Ankur8d304122014-10-07 10:43:43 -0700433
Suharsh Sivakumar32c8e752015-03-31 19:26:28 -0700434It is an error to call 'set default' with blessings whose public key does
Asim Shankar1789b8a2014-10-31 17:31:41 -0700435not match the public key of the principal specified by the environment.
Ankur8d304122014-10-07 10:43:43 -0700436`,
437 ArgsName: "<file>",
438 ArgsLong: `
439<file> is the path to a file containing a blessing typically obtained from
440this tool. - is used for STDIN.
441`,
442 Run: func(cmd *cmdline.Command, args []string) error {
443 if len(args) != 1 {
Asim Shankar66c52f92014-10-15 23:39:10 -0700444 return fmt.Errorf("requires exactly one argument, <file>, provided %d", len(args))
Ankur8d304122014-10-07 10:43:43 -0700445 }
446 blessings, err := decodeBlessings(args[0])
447 if err != nil {
448 return fmt.Errorf("failed to decode provided blessings: %v", err)
449 }
Matt Rosencrantz574d5e12014-11-26 10:01:37 -0800450
Jiri Simsa6ac95222015-02-23 16:11:49 -0800451 ctx, shutdown := v23.Init()
Matt Rosencrantza5ad2722015-01-22 11:17:47 -0800452 defer shutdown()
Matt Rosencrantz574d5e12014-11-26 10:01:37 -0800453
Jiri Simsa6ac95222015-02-23 16:11:49 -0800454 p := v23.GetPrincipal(ctx)
Asim Shankar1789b8a2014-10-31 17:31:41 -0700455 if err := p.BlessingStore().SetDefault(blessings); err != nil {
Ankur8d304122014-10-07 10:43:43 -0700456 return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
457 }
Asim Shankar66c52f92014-10-15 23:39:10 -0700458 if flagAddToRoots {
459 if err := p.AddToRoots(blessings); err != nil {
460 return fmt.Errorf("AddToRoots failed: %v", err)
461 }
462 }
463 return nil
464 },
465 }
466
467 cmdCreate = &cmdline.Command{
468 Name: "create",
469 Short: "Create a new principal and persist it into a directory",
470 Long: `
Ankur4704f5f2014-10-23 12:40:54 -0700471Creates a new principal with a single self-blessed blessing and writes it out
Ankurc24ff422014-12-16 17:59:26 -0800472to the provided directory. The same directory can then be used to set the
Asim Shankar59b8b692015-03-30 01:23:36 -0700473V23_CREDENTIALS environment variable for other vanadium applications.
Ankur4704f5f2014-10-23 12:40:54 -0700474
475The operation fails if the directory already contains a principal. In this case
Ankurc24ff422014-12-16 17:59:26 -0800476the --overwrite flag can be provided to clear the directory and write out the
gauthamtb7bb39b2014-11-10 11:40:41 -0800477new principal.
Ankur4704f5f2014-10-23 12:40:54 -0700478`,
Asim Shankar66c52f92014-10-15 23:39:10 -0700479 ArgsName: "<directory> <blessing>",
480 ArgsLong: `
Ankurc24ff422014-12-16 17:59:26 -0800481 <directory> is the directory to which the new principal will be persisted.
Asim Shankar66c52f92014-10-15 23:39:10 -0700482 <blessing> is the self-blessed blessing that the principal will be setup to use by default.
483 `,
484 Run: func(cmd *cmdline.Command, args []string) error {
485 if len(args) != 2 {
486 return fmt.Errorf("requires exactly two arguments: <directory> and <blessing>, provided %d", len(args))
487 }
488 dir, name := args[0], args[1]
Ankur4704f5f2014-10-23 12:40:54 -0700489 if flagCreateOverwrite {
Ankurc24ff422014-12-16 17:59:26 -0800490 if err := os.RemoveAll(dir); err != nil {
gauthamtb7bb39b2014-11-10 11:40:41 -0800491 return err
492 }
Ankur4704f5f2014-10-23 12:40:54 -0700493 }
Ankurc24ff422014-12-16 17:59:26 -0800494 p, err := vsecurity.CreatePersistentPrincipal(dir, nil)
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -0700495 if err != nil {
496 return err
Asim Shankar66c52f92014-10-15 23:39:10 -0700497 }
498 blessings, err := p.BlessSelf(name)
499 if err != nil {
500 return fmt.Errorf("BlessSelf(%q) failed: %v", name, err)
501 }
Ankurc24ff422014-12-16 17:59:26 -0800502 if err := vsecurity.SetDefaultBlessings(p, blessings); err != nil {
503 return fmt.Errorf("could not set blessings %v as default: %v", blessings, err)
Asim Shankar66c52f92014-10-15 23:39:10 -0700504 }
Ankurc24ff422014-12-16 17:59:26 -0800505 return nil
506 },
507 }
508
509 cmdFork = &cmdline.Command{
510 Name: "fork",
511 Short: "Fork a new principal from the principal that this tool is running as and persist it into a directory",
512 Long: `
513Creates a new principal with a blessing from the principal specified by the
514environment that this tool is running in, and writes it out to the provided
515directory. The blessing that will be extended is the default one from the
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -0800516blesser's store, or specified by the --with flag. Expiration on the blessing
517are controlled via the --for flag. Additional caveats on the blessing are
518controlled with the --caveat flag. The blessing is marked as default and
Ankurc24ff422014-12-16 17:59:26 -0800519shareable with all peers on the new principal's blessing store.
520
521The operation fails if the directory already contains a principal. In this case
522the --overwrite flag can be provided to clear the directory and write out the
523forked principal.
524`,
525 ArgsName: "<directory> <extension>",
526 ArgsLong: `
527 <directory> is the directory to which the forked principal will be persisted.
528 <extension> is the extension under which the forked principal is blessed.
529 `,
530 Run: func(cmd *cmdline.Command, args []string) error {
531 if len(args) != 2 {
532 return fmt.Errorf("requires exactly two arguments: <directory> and <extension>, provided %d", len(args))
Asim Shankar66c52f92014-10-15 23:39:10 -0700533 }
Ankurc24ff422014-12-16 17:59:26 -0800534 dir, extension := args[0], args[1]
535
Jiri Simsa6ac95222015-02-23 16:11:49 -0800536 ctx, shutdown := v23.Init()
Asim Shankara0bba462015-02-20 22:50:51 -0800537 defer shutdown()
538
539 caveats, err := caveatsFromFlags(flagForkFor, &flagForkCaveats)
540 if err != nil {
541 return err
542 }
543 if !flagForkRequireCaveats && len(caveats) == 0 {
544 caveats = []security.Caveat{security.UnconstrainedUse()}
545 }
546 if len(caveats) == 0 {
547 return errNoCaveats
548 }
549 var with security.Blessings
550 if len(flagForkWith) > 0 {
551 if with, err = decodeBlessings(flagForkWith); err != nil {
552 return fmt.Errorf("failed to read blessings from --with=%q: %v", flagForkWith, err)
553 }
554 } else {
Jiri Simsa6ac95222015-02-23 16:11:49 -0800555 with = v23.GetPrincipal(ctx).BlessingStore().Default()
Asim Shankara0bba462015-02-20 22:50:51 -0800556 }
557
Ankurc24ff422014-12-16 17:59:26 -0800558 if flagCreateOverwrite {
559 if err := os.RemoveAll(dir); err != nil {
560 return err
561 }
562 }
563 p, err := vsecurity.CreatePersistentPrincipal(dir, nil)
564 if err != nil {
565 return err
566 }
567
Ankurc24ff422014-12-16 17:59:26 -0800568 key := p.PublicKey()
Jiri Simsa6ac95222015-02-23 16:11:49 -0800569 rp := v23.GetPrincipal(ctx)
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -0800570 blessings, err := rp.Bless(key, with, extension, caveats[0], caveats[1:]...)
Ankurc24ff422014-12-16 17:59:26 -0800571 if err != nil {
572 return fmt.Errorf("Bless(%v, %v, %q, ...) failed: %v", key, with, extension, err)
573 }
574 if err := vsecurity.SetDefaultBlessings(p, blessings); err != nil {
575 return fmt.Errorf("could not set blessings %v as default: %v", blessings, err)
Asim Shankar66c52f92014-10-15 23:39:10 -0700576 }
Ankur8d304122014-10-07 10:43:43 -0700577 return nil
578 },
579 }
Ankur1d46f552014-10-09 12:13:31 -0700580
581 cmdSeekBlessings = &cmdline.Command{
582 Name: "seekblessings",
Suharsh Sivakumard1cc6e02015-03-16 13:58:49 -0700583 Short: "Seek blessings from a web-based Vanadium blessing service",
Ankur1d46f552014-10-09 12:13:31 -0700584 Long: `
Suharsh Sivakumard1cc6e02015-03-16 13:58:49 -0700585Seeks blessings from a web-based Vanadium blesser which
Ankur1d46f552014-10-09 12:13:31 -0700586requires the caller to first authenticate with Google using OAuth. Simply
587run the command to see what happens.
588
Asim Shankar1789b8a2014-10-31 17:31:41 -0700589The blessings are sought for the principal specified by the environment that
590this tool is running in.
Ankur1d46f552014-10-09 12:13:31 -0700591
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700592The blessings obtained are set as default, unless the --set-default flag is
Ankure548f392014-12-08 18:42:41 -0800593set to true, and are also set for sharing with all peers, unless a more
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700594specific peer pattern is provided using the --for-peer flag.
Ankur1d46f552014-10-09 12:13:31 -0700595`,
596 Run: func(cmd *cmdline.Command, args []string) error {
Asim Shankar08ab3892014-10-16 12:04:07 -0700597 // Initialize the runtime first so that any local errors are reported
598 // before the HTTP roundtrips for obtaining the macaroon begin.
Jiri Simsa6ac95222015-02-23 16:11:49 -0800599 ctx, shutdown := v23.Init()
Matt Rosencrantza5ad2722015-01-22 11:17:47 -0800600 defer shutdown()
Matt Rosencrantz574d5e12014-11-26 10:01:37 -0800601
Ankur1d46f552014-10-09 12:13:31 -0700602 blessedChan := make(chan string)
603 defer close(blessedChan)
Suharsh Sivakumara76dba62014-12-22 16:00:34 -0800604 macaroonChan, err := getMacaroonForBlessRPC(flagSeekBlessingsFrom, blessedChan, flagSeekBlessingsBrowser)
Ankur1d46f552014-10-09 12:13:31 -0700605 if err != nil {
Suharsh Sivakumard1cc6e02015-03-16 13:58:49 -0700606 return fmt.Errorf("failed to get macaroon from Vanadium blesser: %v", err)
Ankur1d46f552014-10-09 12:13:31 -0700607 }
Ankur1d46f552014-10-09 12:13:31 -0700608
Suharsh Sivakumar67ef84a2015-02-13 13:04:44 -0800609 blessings, err := exchangeMacaroonForBlessing(ctx, macaroonChan)
Ankur1d46f552014-10-09 12:13:31 -0700610 if err != nil {
Suharsh Sivakumar67ef84a2015-02-13 13:04:44 -0800611 return err
Ankur1d46f552014-10-09 12:13:31 -0700612 }
613 blessedChan <- fmt.Sprint(blessings)
614 // Wait for getTokenForBlessRPC to clean up:
615 <-macaroonChan
616
Jiri Simsa6ac95222015-02-23 16:11:49 -0800617 p := v23.GetPrincipal(ctx)
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -0800618
Asim Shankar66c52f92014-10-15 23:39:10 -0700619 if flagSeekBlessingsSetDefault {
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -0800620 if err := p.BlessingStore().SetDefault(blessings); err != nil {
Ankur1d46f552014-10-09 12:13:31 -0700621 return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
622 }
623 }
Asim Shankar66c52f92014-10-15 23:39:10 -0700624 if pattern := security.BlessingPattern(flagSeekBlessingsForPeer); len(pattern) > 0 {
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -0800625 if _, err := p.BlessingStore().Set(blessings, pattern); err != nil {
Asim Shankar66c52f92014-10-15 23:39:10 -0700626 return fmt.Errorf("failed to set blessings %v for peers %v: %v", blessings, pattern, err)
627 }
628 }
629 if flagAddToRoots {
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -0800630 if err := p.AddToRoots(blessings); err != nil {
Asim Shankar66c52f92014-10-15 23:39:10 -0700631 return fmt.Errorf("AddToRoots failed: %v", err)
632 }
Ankur1d46f552014-10-09 12:13:31 -0700633 }
Asim Shankar3f9bbab2015-02-15 13:51:37 -0800634 fmt.Fprintf(cmd.Stdout(), "Received blessings: %v\n", blessings)
Jiri Simsa37b0a092015-02-13 09:32:39 -0800635 return nil
Ankur1d46f552014-10-09 12:13:31 -0700636 },
637 }
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800638
639 cmdRecvBlessings = &cmdline.Command{
640 Name: "recvblessings",
641 Short: "Receive blessings sent by another principal and use them as the default",
642 Long: `
643Allow another principal (likely a remote process) to bless this one.
644
645This command sets up the invoker (this process) to wait for a blessing
646from another invocation of this tool (remote process) and prints out the
647command to be run as the remote principal.
648
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700649The received blessings are set as default, unless the --set-default flag is
Ankure548f392014-12-08 18:42:41 -0800650set to true, and are also set for sharing with all peers, unless a more
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700651specific peer pattern is provided using the --for-peer flag.
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800652
653TODO(ashankar,cnicolaou): Make this next paragraph possible! Requires
654the ability to obtain the proxied endpoint.
655
656Typically, this command should require no arguments.
657However, if the sender and receiver are on different network domains, it may
Asim Shankarf32d24d2015-04-01 16:34:26 -0700658make sense to use the --v23.proxy flag:
659 principal --v23.proxy=proxy recvblessings
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800660
661The command to be run at the sender is of the form:
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700662 principal bless --remote-key=KEY --remote-token=TOKEN ADDRESS EXTENSION
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800663
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700664The --remote-key flag is used to by the sender to "authenticate" the receiver,
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800665ensuring it blesses the intended recipient and not any attacker that may have
666taken over the address.
667
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700668The --remote-token flag is used by the sender to authenticate itself to the
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800669receiver. This helps ensure that the receiver rejects blessings from senders
670who just happened to guess the network address of the 'recvblessings'
671invocation.
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700672
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700673If the --remote-arg-file flag is provided to recvblessings, the remote key, remote token
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700674and object address of this principal will be written to the specified location.
675This file can be supplied to bless:
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700676 principal bless --remote-arg-file FILE EXTENSION
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700677
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800678`,
679 Run: func(cmd *cmdline.Command, args []string) error {
680 if len(args) != 0 {
681 return fmt.Errorf("command accepts no arguments")
682 }
Matt Rosencrantz574d5e12014-11-26 10:01:37 -0800683
Jiri Simsa6ac95222015-02-23 16:11:49 -0800684 ctx, shutdown := v23.Init()
Matt Rosencrantza5ad2722015-01-22 11:17:47 -0800685 defer shutdown()
Matt Rosencrantz574d5e12014-11-26 10:01:37 -0800686
Jiri Simsa6ac95222015-02-23 16:11:49 -0800687 server, err := v23.NewServer(ctx)
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800688 if err != nil {
689 return fmt.Errorf("failed to create server to listen for blessings: %v", err)
690 }
691 defer server.Stop()
Jiri Simsa6ac95222015-02-23 16:11:49 -0800692 eps, err := server.Listen(v23.GetListenSpec(ctx))
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800693 if err != nil {
694 return fmt.Errorf("failed to setup listening: %v", err)
695 }
696 var token [24]byte
697 if _, err := rand.Read(token[:]); err != nil {
698 return fmt.Errorf("unable to generate token: %v", err)
699 }
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -0800700
Jiri Simsa6ac95222015-02-23 16:11:49 -0800701 p := v23.GetPrincipal(ctx)
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800702 service := &recvBlessingsService{
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -0800703 principal: p,
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800704 token: base64.URLEncoding.EncodeToString(token[:]),
705 notify: make(chan error),
706 }
707 if err := server.Serve("", service, allowAnyone{}); err != nil {
708 return fmt.Errorf("failed to setup service: %v", err)
709 }
710 // Proposed name:
711 extension := fmt.Sprintf("extension%d", int(token[0])<<16|int(token[1])<<8|int(token[2]))
712 fmt.Println("Run the following command on behalf of the principal that will send blessings:")
713 fmt.Println("You may want to adjust flags affecting the caveats on this blessing, for example using")
714 fmt.Println("the --for flag, or change the extension to something more meaningful")
715 fmt.Println()
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700716 if len(flagRemoteArgFile) > 0 {
717 if err := writeRecvBlessingsInfo(flagRemoteArgFile, p.PublicKey().String(), service.token, eps[0].Name()); err != nil {
718 return fmt.Errorf("failed to write recvblessings info to %v: %v", flagRemoteArgFile, err)
719 }
720 fmt.Printf("make %q accessible to the blesser, possibly by copying the file over and then run:\n", flagRemoteArgFile)
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700721 fmt.Printf("principal bless --remote-arg-file=%v %v", flagRemoteArgFile, extension)
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700722 } else {
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700723 fmt.Printf("principal bless --remote-key=%v --remote-token=%v %v %v\n", p.PublicKey(), service.token, eps[0].Name(), extension)
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700724 }
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800725 fmt.Println()
726 fmt.Println("...waiting for sender..")
727 return <-service.notify
728 },
729 }
Ankurcf6a89f2014-10-06 18:33:03 -0700730)
731
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700732func blessArgs(args []string) (tobless, extension, remoteKey, remoteToken string, err error) {
733 if len(flagRemoteArgFile) > 0 && (len(flagBlessRemoteKey)+len(flagBlessRemoteToken) > 0) {
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700734 return "", "", "", "", fmt.Errorf("--remote-key and --remote-token cannot be provided with --remote-arg-file")
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700735 }
736 if (len(flagBlessRemoteKey) == 0) != (len(flagBlessRemoteToken) == 0) {
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700737 return "", "", "", "", fmt.Errorf("either both --remote-key and --remote-token should be set, or neither should")
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -0700738 }
739
740 if len(flagRemoteArgFile) == 0 {
741 tobless, extension = args[0], args[1]
742 remoteKey = flagBlessRemoteKey
743 remoteToken = flagBlessRemoteToken
744 } else if len(flagRemoteArgFile) > 0 {
745 extension = args[0]
746 remoteKey, remoteToken, tobless, err = blessArgsFromFile(flagRemoteArgFile)
747 }
748 return
749}
750
751func blessOverFileSystem(p security.Principal, tobless string, with security.Blessings, extension string, caveats []security.Caveat) (security.Blessings, error) {
752 var key security.PublicKey
753 if finfo, err := os.Stat(tobless); err == nil && finfo.IsDir() {
754 other, err := vsecurity.LoadPersistentPrincipal(tobless, nil)
755 if err != nil {
756 if other, err = vsecurity.CreatePersistentPrincipal(tobless, nil); err != nil {
757 return security.Blessings{}, fmt.Errorf("failed to read principal in directory %q: %v", tobless, err)
758 }
759 }
760 key = other.PublicKey()
761 } else if other, err := decodeBlessings(tobless); err != nil {
762 return security.Blessings{}, fmt.Errorf("failed to decode blessings in %q: %v", tobless, err)
763 } else {
764 key = other.PublicKey()
765 }
766 return p.Bless(key, with, extension, caveats[0], caveats[1:]...)
767}
768
769type recvBlessingsInfo struct {
770 RemoteKey string `json:remote_key`
771 RemoteToken string `json:remote_token`
772 Name string `json:name`
773}
774
775func writeRecvBlessingsInfo(fname string, remoteKey, remoteToken, name string) error {
776 f, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE, 0666)
777 if err != nil {
778 return err
779 }
780 b, err := json.Marshal(recvBlessingsInfo{remoteKey, remoteToken, name})
781 if err != nil {
782 return err
783 }
784 if _, err := f.Write(b); err != nil {
785 return err
786 }
787 return nil
788}
789
790func blessArgsFromFile(fname string) (remoteKey, remoteToken, tobless string, err error) {
791 blessJSON, err := ioutil.ReadFile(fname)
792 if err != nil {
793 return "", "", "", err
794 }
795 var binfo recvBlessingsInfo
796 if err := json.Unmarshal(blessJSON, &binfo); err != nil {
797 return "", "", "", err
798 }
799 return binfo.RemoteKey, binfo.RemoteToken, binfo.Name, err
800}
801
Ankurcf6a89f2014-10-06 18:33:03 -0700802func main() {
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -0800803 cmdBlessSelf.Flags.Var(&flagBlessSelfCaveats, "caveat", flagBlessSelfCaveats.usage())
Asim Shankara0bba462015-02-20 22:50:51 -0800804 cmdBlessSelf.Flags.DurationVar(&flagBlessSelfFor, "for", 0, "Duration of blessing validity (zero implies no expiration)")
805
Ankur77c32ac2014-12-18 14:18:19 -0800806 cmdFork.Flags.BoolVar(&flagCreateOverwrite, "overwrite", false, "If true, any existing principal data in the directory will be overwritten")
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -0800807 cmdFork.Flags.Var(&flagForkCaveats, "caveat", flagForkCaveats.usage())
Asim Shankara0bba462015-02-20 22:50:51 -0800808 cmdFork.Flags.DurationVar(&flagForkFor, "for", 0, "Duration of blessing validity (zero implies no expiration caveat)")
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700809 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")
Ankur77c32ac2014-12-18 14:18:19 -0800810 cmdFork.Flags.StringVar(&flagForkWith, "with", "", "Path to file containing blessing to extend")
811
Suharsh Sivakumar1d61f642015-02-17 20:56:14 -0800812 cmdBless.Flags.Var(&flagBlessCaveats, "caveat", flagBlessCaveats.usage())
Asim Shankara0bba462015-02-20 22:50:51 -0800813 cmdBless.Flags.DurationVar(&flagBlessFor, "for", 0, "Duration of blessing validity (zero implies no expiration caveat)")
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700814 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")
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800815 cmdBless.Flags.StringVar(&flagBlessWith, "with", "", "Path to file containing blessing to extend")
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700816 cmdBless.Flags.StringVar(&flagBlessRemoteKey, "remote-key", "", "Public key of the remote principal to bless (obtained from the 'recvblessings' command run by the remote principal")
817 cmdBless.Flags.StringVar(&flagBlessRemoteToken, "remote-token", "", "Token provided by principal running the 'recvblessings' command")
818 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>.")
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800819
Suharsh Sivakumar33884012015-03-25 13:27:55 -0700820 cmdSeekBlessings.Flags.StringVar(&flagSeekBlessingsFrom, "from", "https://dev.v.io/auth/google", "URL to use to begin the seek blessings process")
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700821 cmdSeekBlessings.Flags.BoolVar(&flagSeekBlessingsSetDefault, "set-default", true, "If true, the blessings obtained will be set as the default blessing in the store")
822 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")
Suharsh Sivakumara76dba62014-12-22 16:00:34 -0800823 cmdSeekBlessings.Flags.BoolVar(&flagSeekBlessingsBrowser, "browser", true, "If false, the seekblessings command will not open the browser and only print the url to visit.")
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700824 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")
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800825
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700826 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")
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800827
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700828 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")
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800829
Ankur4704f5f2014-10-23 12:40:54 -0700830 cmdCreate.Flags.BoolVar(&flagCreateOverwrite, "overwrite", false, "If true, any existing principal data in the directory will be overwritten")
Asim Shankar66c52f92014-10-15 23:39:10 -0700831
Suharsh Sivakumared5be1d2015-04-01 17:45:35 -0700832 cmdRecvBlessings.Flags.BoolVar(&flagRecvBlessingsSetDefault, "set-default", true, "If true, the blessings received will be set as the default blessing in the store")
833 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")
834 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'.")
Ankure548f392014-12-08 18:42:41 -0800835
Suharsh Sivakumar32c8e752015-03-31 19:26:28 -0700836 cmdSet := &cmdline.Command{
837 Name: "set",
838 Short: "Mutate the principal's blessings.",
Asim Shankar66c52f92014-10-15 23:39:10 -0700839 Long: `
Suharsh Sivakumar32c8e752015-03-31 19:26:28 -0700840Commands to mutate the blessings of the principal.
Asim Shankar66c52f92014-10-15 23:39:10 -0700841
Suharsh Sivakumar32c8e752015-03-31 19:26:28 -0700842All input blessings are expected to be serialized using base64-VOM-encoding.
843See 'principal get'.
Asim Shankar66c52f92014-10-15 23:39:10 -0700844`,
Suharsh Sivakumar32c8e752015-03-31 19:26:28 -0700845 Children: []*cmdline.Command{cmdSetDefault, cmdSetForPeer},
846 }
847
848 cmdGet := &cmdline.Command{
849 Name: "get",
850 Short: "Read the principal's blessings.",
851 Long: `
852Commands to inspect the blessings of the principal.
853
854All blessings are printed to stdout using base64-VOM-encoding.
855`,
856 Children: []*cmdline.Command{cmdGetDefault, cmdGetForPeer},
Ankurcf6a89f2014-10-06 18:33:03 -0700857 }
Ankurcf6a89f2014-10-06 18:33:03 -0700858
Matt Rosencrantz92303e12015-01-21 09:02:42 -0800859 root := &cmdline.Command{
Ankurcf6a89f2014-10-06 18:33:03 -0700860 Name: "principal",
Suharsh Sivakumard1cc6e02015-03-16 13:58:49 -0700861 Short: "Create and manage vanadium principals",
Ankurcf6a89f2014-10-06 18:33:03 -0700862 Long: `
863The principal tool helps create and manage blessings and the set of trusted
864roots bound to a principal.
865
866All objects are printed using base64-VOM-encoding.
867`,
Suharsh Sivakumar32c8e752015-03-31 19:26:28 -0700868 Children: []*cmdline.Command{cmdCreate, cmdFork, cmdSeekBlessings, cmdRecvBlessings, cmdDump, cmdDumpBlessings, cmdBlessSelf, cmdBless, cmdSet, cmdGet, cmdAddToRoots},
Matt Rosencrantz92303e12015-01-21 09:02:42 -0800869 }
870 os.Exit(root.Main())
Ankurcf6a89f2014-10-06 18:33:03 -0700871}
872
Ankurcf6a89f2014-10-06 18:33:03 -0700873func decodeBlessings(fname string) (security.Blessings, error) {
Asim Shankarb07ec692015-02-27 23:40:44 -0800874 var b security.Blessings
875 err := decode(fname, &b)
876 return b, err
Ankurcf6a89f2014-10-06 18:33:03 -0700877}
878
879func dumpBlessings(blessings security.Blessings) error {
Asim Shankar2bf7b1e2015-02-27 00:45:12 -0800880 if blessings.IsZero() {
881 return fmt.Errorf("no blessings found")
Ankurcf6a89f2014-10-06 18:33:03 -0700882 }
Asim Shankarb07ec692015-02-27 23:40:44 -0800883 str, err := base64VomEncode(blessings)
Ankurcf6a89f2014-10-06 18:33:03 -0700884 if err != nil {
885 return fmt.Errorf("base64-VOM encoding failed: %v", err)
886 }
887 fmt.Println(str)
888 return nil
889}
890
891func read(fname string) (string, error) {
892 if len(fname) == 0 {
893 return "", nil
894 }
895 f := os.Stdin
896 if fname != "-" {
897 var err error
898 if f, err = os.Open(fname); err != nil {
899 return "", fmt.Errorf("failed to open %q: %v", fname, err)
900 }
901 }
902 defer f.Close()
903 var buf bytes.Buffer
904 if _, err := io.Copy(&buf, f); err != nil {
905 return "", fmt.Errorf("failed to read %q: %v", fname, err)
906 }
907 return buf.String(), nil
908}
909
910func decode(fname string, val interface{}) error {
911 str, err := read(fname)
912 if err != nil {
913 return err
914 }
Asim Shankarb3a82ba2014-10-29 11:41:27 -0700915 if err := base64VomDecode(str, val); err != nil || val == nil {
Ankurcf6a89f2014-10-06 18:33:03 -0700916 return fmt.Errorf("failed to decode %q: %v", fname, err)
917 }
918 return nil
919}
920
921func defaultBlessingName() string {
922 var name string
923 if user, _ := user.Current(); user != nil && len(user.Username) > 0 {
924 name = user.Username
925 } else {
926 name = "anonymous"
927 }
928 if host, _ := os.Hostname(); len(host) > 0 {
929 name = name + "@" + host
930 }
931 return name
932}
Asim Shankardf88a2e2014-10-21 17:20:28 -0700933
934func rootkey(chain []security.Certificate) string {
935 if len(chain) == 0 {
936 return "<empty certificate chain>"
937 }
938 key, err := security.UnmarshalPublicKey(chain[0].PublicKey)
939 if err != nil {
940 return fmt.Sprintf("<invalid PublicKey: %v>", err)
941 }
942 return fmt.Sprintf("%v", key)
943}
Asim Shankarb3a82ba2014-10-29 11:41:27 -0700944
945func base64VomEncode(i interface{}) (string, error) {
946 buf := &bytes.Buffer{}
947 closer := base64.NewEncoder(base64.URLEncoding, buf)
Todd Wang8e17bff2015-02-18 11:18:56 -0800948 enc, err := vom.NewEncoder(closer)
Suharsh Sivakumar12b089d2015-01-02 14:17:12 -0800949 if err != nil {
950 return "", err
951 }
952 if err := enc.Encode(i); err != nil {
Asim Shankarb3a82ba2014-10-29 11:41:27 -0700953 return "", err
954 }
955 // Must close the base64 encoder to flush out any partially written
956 // blocks.
957 if err := closer.Close(); err != nil {
958 return "", err
959 }
960 return buf.String(), nil
961}
962
963func base64VomDecode(s string, i interface{}) error {
964 b, err := base64.URLEncoding.DecodeString(s)
965 if err != nil {
966 return err
967 }
Todd Wang3425a902015-01-21 18:43:59 -0800968 dec, err := vom.NewDecoder(bytes.NewBuffer(b))
Suharsh Sivakumar12b089d2015-01-02 14:17:12 -0800969 if err != nil {
970 return err
971 }
972 return dec.Decode(i)
Asim Shankarb3a82ba2014-10-29 11:41:27 -0700973}
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800974
975type recvBlessingsService struct {
976 principal security.Principal
977 notify chan error
978 token string
979}
980
Matt Rosencrantz94502cf2015-03-18 09:43:44 -0700981func (r *recvBlessingsService) Grant(call rpc.StreamServerCall, token string) error {
Suharsh Sivakumar380bf342015-02-27 15:38:27 -0800982 b := call.GrantedBlessings()
Asim Shankar2bf7b1e2015-02-27 00:45:12 -0800983 if b.IsZero() {
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800984 return fmt.Errorf("no blessings granted by sender")
985 }
986 if len(token) != len(r.token) {
987 // A timing attack can be used to figure out the length
988 // of the token, but then again, so can looking at the
989 // source code. So, it's okay.
990 return fmt.Errorf("blessings received from unexpected sender")
991 }
992 if subtle.ConstantTimeCompare([]byte(token), []byte(r.token)) != 1 {
993 return fmt.Errorf("blessings received from unexpected sender")
994 }
Ankure548f392014-12-08 18:42:41 -0800995 if flagRecvBlessingsSetDefault {
996 if err := r.principal.BlessingStore().SetDefault(b); err != nil {
997 return fmt.Errorf("failed to set blessings %v as default: %v", b, err)
998 }
Asim Shankarf11b1bc2014-11-12 17:18:45 -0800999 }
Ankure548f392014-12-08 18:42:41 -08001000 if pattern := security.BlessingPattern(flagRecvBlessingsForPeer); len(pattern) > 0 {
1001 if _, err := r.principal.BlessingStore().Set(b, pattern); err != nil {
1002 return fmt.Errorf("failed to set blessings %v for peers %v: %v", b, pattern, err)
1003 }
Asim Shankarf11b1bc2014-11-12 17:18:45 -08001004 }
1005 if flagAddToRoots {
1006 if err := r.principal.AddToRoots(b); err != nil {
1007 return fmt.Errorf("failed to add blessings to recognized roots: %v", err)
1008 }
1009 }
1010 fmt.Println("Received blessings:", b)
1011 r.notify <- nil
1012 return nil
1013}
1014
1015type allowAnyone struct{}
1016
Matt Rosencrantz250558f2015-03-17 11:37:31 -07001017func (allowAnyone) Authorize(*context.T) error { return nil }
Asim Shankarf11b1bc2014-11-12 17:18:45 -08001018
1019type granter struct {
1020 p security.Principal
1021 with security.Blessings
1022 extension string
1023 caveats []security.Caveat
1024 serverKey string
1025}
1026
1027func (g *granter) Grant(server security.Blessings) (security.Blessings, error) {
1028 if got := fmt.Sprintf("%v", server.PublicKey()); got != g.serverKey {
Matt Rosencrantz94502cf2015-03-18 09:43:44 -07001029 // If the granter returns an error, the RPC framework should
Asim Shankarf11b1bc2014-11-12 17:18:45 -08001030 // abort the RPC before sending the request to the server.
1031 // Thus, there is no concern about leaking the token to an
1032 // imposter server.
Asim Shankar2bf7b1e2015-02-27 00:45:12 -08001033 return security.Blessings{}, fmt.Errorf("key mismatch: Remote end has public key %v, want %v", got, g.serverKey)
Asim Shankarf11b1bc2014-11-12 17:18:45 -08001034 }
1035 return g.p.Bless(server.PublicKey(), g.with, g.extension, g.caveats[0], g.caveats[1:]...)
1036}
Matt Rosencrantz94502cf2015-03-18 09:43:44 -07001037func (*granter) RPCCallOpt() {}
Asim Shankarf11b1bc2014-11-12 17:18:45 -08001038
Suharsh Sivakumarab21eb02015-04-01 12:58:20 -07001039func blessOverNetwork(ctx *context.T, object string, granter *granter, remoteToken string) error {
Jiri Simsa6ac95222015-02-23 16:11:49 -08001040 client := v23.GetClient(ctx)
Asim Shankar263c73b2015-03-19 18:31:26 -07001041 // The receiver is being authorized based on the hash of its public key
1042 // (see Grant), so it should be fine to ignore the blessing names in the endpoint
1043 // (which are likely to not be recognized by the sender anyway).
1044 //
1045 // At worst, there is a privacy leak of the senders intent to send some
1046 // blessings. That could be addressed by making the full public key of
1047 // the recipeint available to the sender and using
1048 // options.ServerPublicKey instead of providing a "hash" of the
1049 // recipients public key and verifying in the Granter implementation.
1050 call, err := client.StartCall(ctx, object, "Grant", []interface{}{remoteToken}, granter, options.SkipServerEndpointAuthorization{})
Asim Shankarf11b1bc2014-11-12 17:18:45 -08001051 if err != nil {
1052 return fmt.Errorf("failed to start RPC to %q: %v", object, err)
1053 }
Todd Wange77f9952015-02-18 13:20:50 -08001054 if err := call.Finish(); err != nil {
1055 return fmt.Errorf("failed to finish RPC to %q: %v", object, err)
Asim Shankarf11b1bc2014-11-12 17:18:45 -08001056 }
Todd Wange77f9952015-02-18 13:20:50 -08001057 return nil
Asim Shankarf11b1bc2014-11-12 17:18:45 -08001058}
Asim Shankara0bba462015-02-20 22:50:51 -08001059
1060func caveatsFromFlags(expiry time.Duration, caveatsFlag *caveatsFlag) ([]security.Caveat, error) {
1061 caveats, err := caveatsFlag.Compile()
1062 if err != nil {
1063 return nil, fmt.Errorf("failed to parse caveats: %v", err)
1064 }
1065 if expiry > 0 {
1066 ecav, err := security.ExpiryCaveat(time.Now().Add(expiry))
1067 if err != nil {
1068 return nil, fmt.Errorf("failed to create expiration caveat: %v", err)
1069 }
1070 caveats = append(caveats, ecav)
1071 }
1072 return caveats, nil
1073}
Asim Shankarb07ec692015-02-27 23:40:44 -08001074
1075// Circuitous route to get to the certificate chains.
1076// See comments on why security.MarshalBlessings is discouraged.
1077// Though, a better alternative is worth looking into.
1078func blessings2wire(b security.Blessings) (security.WireBlessings, error) {
1079 var wire security.WireBlessings
1080 data, err := vom.Encode(b)
1081 if err != nil {
1082 return wire, err
1083 }
1084 err = vom.Decode(data, &wire)
1085 return wire, err
1086}