Jiri Simsa | d7616c9 | 2015-03-24 23:44:30 -0700 | [diff] [blame] | 1 | // 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 | |
Todd Wang | 8850968 | 2015-04-10 10:28:24 -0700 | [diff] [blame] | 5 | // Package agentlib implements a client for communicating with an agentd process |
| 6 | // holding the private key for an identity. |
| 7 | package agentlib |
Ryan Brown | fed691e | 2014-09-15 13:09:40 -0700 | [diff] [blame] | 8 | |
| 9 | import ( |
| 10 | "fmt" |
Ryan Brown | 50b473a | 2014-09-23 14:23:00 -0700 | [diff] [blame] | 11 | "net" |
| 12 | "os" |
Ryan Brown | 7f950a8 | 2015-04-20 18:08:39 -0700 | [diff] [blame] | 13 | "strconv" |
| 14 | "syscall" |
Ryan Brown | fed691e | 2014-09-15 13:09:40 -0700 | [diff] [blame] | 15 | |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 16 | "v.io/v23/context" |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 17 | "v.io/v23/naming" |
| 18 | "v.io/v23/options" |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 19 | "v.io/v23/rpc" |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 20 | "v.io/v23/security" |
Ryan Brown | 7f950a8 | 2015-04-20 18:08:39 -0700 | [diff] [blame] | 21 | "v.io/v23/verror" |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 22 | "v.io/v23/vtrace" |
Jiri Simsa | 337af23 | 2015-02-27 14:36:46 -0800 | [diff] [blame] | 23 | "v.io/x/lib/vlog" |
Todd Wang | b351149 | 2015-04-07 23:32:34 -0700 | [diff] [blame] | 24 | "v.io/x/ref/services/agent/internal/cache" |
Todd Wang | fb93903 | 2015-04-08 16:42:44 -0700 | [diff] [blame] | 25 | "v.io/x/ref/services/agent/internal/unixfd" |
Ryan Brown | fed691e | 2014-09-15 13:09:40 -0700 | [diff] [blame] | 26 | ) |
| 27 | |
Ryan Brown | 7f950a8 | 2015-04-20 18:08:39 -0700 | [diff] [blame] | 28 | const pkgPath = "v.io/x/ref/services/agent/agentlib" |
| 29 | |
| 30 | // Errors |
| 31 | var ( |
| 32 | errInvalidProtocol = verror.Register(pkgPath+".errInvalidProtocol", |
| 33 | verror.NoRetry, "{1:}{2:} invalid agent protocol {3}") |
| 34 | ) |
Ryan Brown | fed691e | 2014-09-15 13:09:40 -0700 | [diff] [blame] | 35 | |
| 36 | type client struct { |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 37 | caller caller |
Ryan Brown | fed691e | 2014-09-15 13:09:40 -0700 | [diff] [blame] | 38 | key security.PublicKey |
| 39 | } |
| 40 | |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 41 | type caller struct { |
Matt Rosencrantz | 6edab56 | 2015-01-12 11:07:55 -0800 | [diff] [blame] | 42 | ctx *context.T |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 43 | client rpc.Client |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 44 | name string |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 45 | } |
| 46 | |
Todd Wang | e77f995 | 2015-02-18 13:20:50 -0800 | [diff] [blame] | 47 | func (c *caller) call(name string, results []interface{}, args ...interface{}) error { |
| 48 | call, err := c.startCall(name, args...) |
| 49 | if err != nil { |
| 50 | return err |
Ryan Brown | fed691e | 2014-09-15 13:09:40 -0700 | [diff] [blame] | 51 | } |
Todd Wang | e77f995 | 2015-02-18 13:20:50 -0800 | [diff] [blame] | 52 | if err := call.Finish(results...); err != nil { |
| 53 | return err |
| 54 | } |
| 55 | return nil |
Ryan Brown | fed691e | 2014-09-15 13:09:40 -0700 | [diff] [blame] | 56 | } |
| 57 | |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 58 | func (c *caller) startCall(name string, args ...interface{}) (rpc.ClientCall, error) { |
Todd Wang | ad49204 | 2015-04-17 15:58:40 -0700 | [diff] [blame] | 59 | ctx, _ := vtrace.WithNewTrace(c.ctx) |
Suharsh Sivakumar | 2c5d810 | 2015-03-23 08:49:12 -0700 | [diff] [blame] | 60 | // SecurityNone is safe here since we're using anonymous unix sockets. |
| 61 | return c.client.StartCall(ctx, c.name, name, args, options.SecurityNone, options.NoResolve{}) |
Ryan Brown | 81bcb3a | 2015-02-11 10:58:01 -0800 | [diff] [blame] | 62 | } |
| 63 | |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 64 | func results(inputs ...interface{}) []interface{} { |
Todd Wang | e77f995 | 2015-02-18 13:20:50 -0800 | [diff] [blame] | 65 | return inputs |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 66 | } |
| 67 | |
| 68 | // NewAgentPrincipal returns a security.Pricipal using the PrivateKey held in a remote agent process. |
Ryan Brown | 7f950a8 | 2015-04-20 18:08:39 -0700 | [diff] [blame] | 69 | // 'endpoint' is the endpoint for connecting to the agent, typically obtained from |
| 70 | // os.GetEnv(envvar.AgentEndpoint). |
Bogdan Caprita | 6613fc4 | 2015-01-28 11:54:23 -0800 | [diff] [blame] | 71 | // 'ctx' should not have a deadline, and should never be cancelled while the |
| 72 | // principal is in use. |
Ryan Brown | 7f950a8 | 2015-04-20 18:08:39 -0700 | [diff] [blame] | 73 | func NewAgentPrincipal(ctx *context.T, endpoint naming.Endpoint, insecureClient rpc.Client) (security.Principal, error) { |
| 74 | p, err := newUncachedPrincipal(ctx, endpoint, insecureClient) |
Ryan Brown | 81bcb3a | 2015-02-11 10:58:01 -0800 | [diff] [blame] | 75 | if err != nil { |
| 76 | return p, err |
| 77 | } |
| 78 | call, callErr := p.caller.startCall("NotifyWhenChanged") |
| 79 | if callErr != nil { |
| 80 | return nil, callErr |
| 81 | } |
| 82 | return cache.NewCachedPrincipal(p.caller.ctx, p, call) |
| 83 | } |
Ryan Brown | 7f950a8 | 2015-04-20 18:08:39 -0700 | [diff] [blame] | 84 | func newUncachedPrincipal(ctx *context.T, ep naming.Endpoint, insecureClient rpc.Client) (*client, error) { |
| 85 | // This isn't a real vanadium endpoint. It contains the vanadium version |
| 86 | // info, but the address is serving the agent protocol. |
| 87 | if ep.Addr().Network() != "" { |
| 88 | return nil, verror.New(errInvalidProtocol, ctx, ep.Addr().Network()) |
| 89 | } |
| 90 | fd, err := strconv.Atoi(ep.Addr().String()) |
| 91 | if err != nil { |
| 92 | return nil, err |
| 93 | } |
| 94 | syscall.ForkLock.Lock() |
| 95 | fd, err = syscall.Dup(fd) |
| 96 | if err == nil { |
| 97 | syscall.CloseOnExec(fd) |
| 98 | } |
| 99 | syscall.ForkLock.Unlock() |
| 100 | if err != nil { |
| 101 | return nil, err |
| 102 | } |
Ryan Brown | 8178944 | 2014-10-30 13:23:53 -0700 | [diff] [blame] | 103 | f := os.NewFile(uintptr(fd), "agent_client") |
| 104 | defer f.Close() |
| 105 | conn, err := net.FileConn(f) |
Ryan Brown | 50b473a | 2014-09-23 14:23:00 -0700 | [diff] [blame] | 106 | if err != nil { |
| 107 | return nil, err |
| 108 | } |
| 109 | // This is just an arbitrary 1 byte string. The value is ignored. |
| 110 | data := make([]byte, 1) |
Bogdan Caprita | bb37c54 | 2015-01-22 10:21:57 -0800 | [diff] [blame] | 111 | addr, err := unixfd.SendConnection(conn.(*net.UnixConn), data) |
Ryan Brown | 50b473a | 2014-09-23 14:23:00 -0700 | [diff] [blame] | 112 | if err != nil { |
| 113 | return nil, err |
| 114 | } |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 115 | caller := caller{ |
Matt Rosencrantz | 99cc06e | 2015-01-16 10:25:11 -0800 | [diff] [blame] | 116 | client: insecureClient, |
Ryan Brown | 7f950a8 | 2015-04-20 18:08:39 -0700 | [diff] [blame] | 117 | name: naming.JoinAddressName(agentEndpoint("unixfd", addr.String()), ""), |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 118 | ctx: ctx, |
| 119 | } |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 120 | agent := &client{caller: caller} |
Ryan Brown | fed691e | 2014-09-15 13:09:40 -0700 | [diff] [blame] | 121 | if err := agent.fetchPublicKey(); err != nil { |
| 122 | return nil, err |
| 123 | } |
| 124 | return agent, nil |
| 125 | } |
| 126 | |
| 127 | func (c *client) fetchPublicKey() (err error) { |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 128 | var b []byte |
| 129 | if err = c.caller.call("PublicKey", results(&b)); err != nil { |
Ryan Brown | fed691e | 2014-09-15 13:09:40 -0700 | [diff] [blame] | 130 | return |
| 131 | } |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 132 | c.key, err = security.UnmarshalPublicKey(b) |
Ryan Brown | fed691e | 2014-09-15 13:09:40 -0700 | [diff] [blame] | 133 | return |
| 134 | } |
| 135 | |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 136 | func (c *client) Bless(key security.PublicKey, with security.Blessings, extension string, caveat security.Caveat, additionalCaveats ...security.Caveat) (security.Blessings, error) { |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 137 | var blessings security.Blessings |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 138 | marshalledKey, err := key.MarshalBinary() |
| 139 | if err != nil { |
Asim Shankar | 2bf7b1e | 2015-02-27 00:45:12 -0800 | [diff] [blame] | 140 | return security.Blessings{}, err |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 141 | } |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 142 | err = c.caller.call("Bless", results(&blessings), marshalledKey, with, extension, caveat, additionalCaveats) |
| 143 | return blessings, err |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 144 | } |
| 145 | |
| 146 | func (c *client) BlessSelf(name string, caveats ...security.Caveat) (security.Blessings, error) { |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 147 | var blessings security.Blessings |
| 148 | err := c.caller.call("BlessSelf", results(&blessings), name, caveats) |
| 149 | return blessings, err |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 150 | } |
| 151 | |
| 152 | func (c *client) Sign(message []byte) (sig security.Signature, err error) { |
| 153 | err = c.caller.call("Sign", results(&sig), message) |
| 154 | return |
| 155 | } |
| 156 | |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 157 | func (c *client) MintDischarge(forCaveat, caveatOnDischarge security.Caveat, additionalCaveatsOnDischarge ...security.Caveat) (security.Discharge, error) { |
Asim Shankar | 0864282 | 2015-03-02 21:21:09 -0800 | [diff] [blame] | 158 | var discharge security.Discharge |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 159 | if err := c.caller.call("MintDischarge", results(&discharge), forCaveat, caveatOnDischarge, additionalCaveatsOnDischarge); err != nil { |
Asim Shankar | 3ad0b8a | 2015-02-25 00:37:21 -0800 | [diff] [blame] | 160 | return security.Discharge{}, err |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 161 | } |
Asim Shankar | 0864282 | 2015-03-02 21:21:09 -0800 | [diff] [blame] | 162 | return discharge, nil |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 163 | } |
| 164 | |
Ryan Brown | fed691e | 2014-09-15 13:09:40 -0700 | [diff] [blame] | 165 | func (c *client) PublicKey() security.PublicKey { |
| 166 | return c.key |
| 167 | } |
| 168 | |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 169 | func (c *client) BlessingsByName(pattern security.BlessingPattern) []security.Blessings { |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 170 | var blessings []security.Blessings |
| 171 | if err := c.caller.call("BlessingsByName", results(&blessings), pattern); err != nil { |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 172 | vlog.Errorf("error calling BlessingsByName: %v", err) |
| 173 | return nil |
| 174 | } |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 175 | return blessings |
| 176 | } |
| 177 | |
gauthamt | 8dc9a18 | 2015-01-08 18:03:18 -0800 | [diff] [blame] | 178 | func (c *client) BlessingsInfo(blessings security.Blessings) map[string][]security.Caveat { |
| 179 | var bInfo map[string][]security.Caveat |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 180 | err := c.caller.call("BlessingsInfo", results(&bInfo), blessings) |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 181 | if err != nil { |
| 182 | vlog.Errorf("error calling BlessingsInfo: %v", err) |
| 183 | return nil |
| 184 | } |
gauthamt | 8dc9a18 | 2015-01-08 18:03:18 -0800 | [diff] [blame] | 185 | return bInfo |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 186 | } |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 187 | func (c *client) BlessingStore() security.BlessingStore { |
| 188 | return &blessingStore{c.caller, c.key} |
| 189 | } |
| 190 | |
| 191 | func (c *client) Roots() security.BlessingRoots { |
| 192 | return &blessingRoots{c.caller} |
| 193 | } |
| 194 | |
| 195 | func (c *client) AddToRoots(blessings security.Blessings) error { |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 196 | return c.caller.call("AddToRoots", results(), blessings) |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 197 | } |
| 198 | |
| 199 | type blessingStore struct { |
| 200 | caller caller |
| 201 | key security.PublicKey |
| 202 | } |
| 203 | |
| 204 | func (b *blessingStore) Set(blessings security.Blessings, forPeers security.BlessingPattern) (security.Blessings, error) { |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 205 | var previous security.Blessings |
| 206 | err := b.caller.call("BlessingStoreSet", results(&previous), blessings, forPeers) |
| 207 | return previous, err |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 208 | } |
| 209 | |
| 210 | func (b *blessingStore) ForPeer(peerBlessings ...string) security.Blessings { |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 211 | var blessings security.Blessings |
| 212 | if err := b.caller.call("BlessingStoreForPeer", results(&blessings), peerBlessings); err != nil { |
| 213 | vlog.Errorf("error calling BlessingStorePeerBlessings: %v", err) |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 214 | } |
| 215 | return blessings |
| 216 | } |
| 217 | |
| 218 | func (b *blessingStore) SetDefault(blessings security.Blessings) error { |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 219 | return b.caller.call("BlessingStoreSetDefault", results(), blessings) |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 220 | } |
| 221 | |
| 222 | func (b *blessingStore) Default() security.Blessings { |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 223 | var blessings security.Blessings |
| 224 | err := b.caller.call("BlessingStoreDefault", results(&blessings)) |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 225 | if err != nil { |
| 226 | vlog.Errorf("error calling BlessingStoreDefault: %v", err) |
Asim Shankar | 2bf7b1e | 2015-02-27 00:45:12 -0800 | [diff] [blame] | 227 | return security.Blessings{} |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 228 | } |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 229 | return blessings |
| 230 | } |
| 231 | |
| 232 | func (b *blessingStore) PublicKey() security.PublicKey { |
| 233 | return b.key |
| 234 | } |
| 235 | |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 236 | func (b *blessingStore) PeerBlessings() map[security.BlessingPattern]security.Blessings { |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 237 | var bmap map[security.BlessingPattern]security.Blessings |
| 238 | err := b.caller.call("BlessingStorePeerBlessings", results(&bmap)) |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 239 | if err != nil { |
| 240 | vlog.Errorf("error calling BlessingStorePeerBlessings: %v", err) |
| 241 | return nil |
| 242 | } |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 243 | return bmap |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 244 | } |
| 245 | |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 246 | func (b *blessingStore) DebugString() (s string) { |
| 247 | err := b.caller.call("BlessingStoreDebugString", results(&s)) |
| 248 | if err != nil { |
| 249 | s = fmt.Sprintf("error calling BlessingStoreDebugString: %v", err) |
| 250 | vlog.Errorf(s) |
| 251 | } |
| 252 | return |
| 253 | } |
| 254 | |
| 255 | type blessingRoots struct { |
| 256 | caller caller |
| 257 | } |
| 258 | |
| 259 | func (b *blessingRoots) Add(root security.PublicKey, pattern security.BlessingPattern) error { |
| 260 | marshalledKey, err := root.MarshalBinary() |
| 261 | if err != nil { |
| 262 | return err |
| 263 | } |
| 264 | return b.caller.call("BlessingRootsAdd", results(), marshalledKey, pattern) |
| 265 | } |
| 266 | |
| 267 | func (b *blessingRoots) Recognized(root security.PublicKey, blessing string) error { |
| 268 | marshalledKey, err := root.MarshalBinary() |
| 269 | if err != nil { |
| 270 | return err |
| 271 | } |
Asim Shankar | b378e66 | 2015-01-16 10:50:48 -0800 | [diff] [blame] | 272 | return b.caller.call("BlessingRootsRecognized", results(), marshalledKey, blessing) |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 273 | } |
| 274 | |
| 275 | func (b *blessingRoots) DebugString() (s string) { |
| 276 | err := b.caller.call("BlessingRootsDebugString", results(&s)) |
| 277 | if err != nil { |
| 278 | s = fmt.Sprintf("error calling BlessingRootsDebugString: %v", err) |
| 279 | vlog.Errorf(s) |
| 280 | } |
Ryan Brown | fed691e | 2014-09-15 13:09:40 -0700 | [diff] [blame] | 281 | return |
| 282 | } |
Ryan Brown | 7f950a8 | 2015-04-20 18:08:39 -0700 | [diff] [blame] | 283 | |
| 284 | func agentEndpoint(proto, addr string) string { |
| 285 | // TODO: use naming.FormatEndpoint when it supports version 5. |
| 286 | return fmt.Sprintf("@5@%s@%s@@s@@@", proto, addr) |
| 287 | } |
| 288 | |
| 289 | func AgentEndpoint(fd int) string { |
| 290 | // We use an empty protocol here because this isn't really speaking |
| 291 | // veyron rpc. |
| 292 | return agentEndpoint("", fmt.Sprintf("%d", fd)) |
| 293 | } |