blob: 1e5bcbb6ad6917d4c336e9256b553cefcacb38f7 [file] [log] [blame]
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001package impl
2
3import (
Gautham6fe61e52014-09-16 13:58:17 -07004 "bytes"
5 "crypto/md5"
6 "encoding/hex"
Bogdan Capritac87a9142014-07-21 10:38:13 -07007 "fmt"
Gautham6fe61e52014-09-16 13:58:17 -07008 "io/ioutil"
Gautham82bb9952014-08-28 14:11:51 -07009 "os"
Robert Kroegeracc778b2014-11-03 17:17:21 -080010 "path"
Gautham82bb9952014-08-28 14:11:51 -070011 "path/filepath"
Bogdan Caprita4d67c042014-08-19 10:41:19 -070012 "strings"
Gautham6fe61e52014-09-16 13:58:17 -070013 "sync"
Jiri Simsa24e87aa2014-06-09 09:27:34 -070014
Bogdan Caprita7f491672014-11-13 14:51:08 -080015 "veyron.io/veyron/veyron/security/agent"
16 "veyron.io/veyron/veyron/security/agent/keymgr"
Jiri Simsa519c5072014-09-17 21:37:57 -070017 vflag "veyron.io/veyron/veyron/security/flag"
18 "veyron.io/veyron/veyron/security/serialization"
Bogdan Capritaa456f472014-12-10 10:18:03 -080019 idevice "veyron.io/veyron/veyron/services/mgmt/device"
20 "veyron.io/veyron/veyron/services/mgmt/device/config"
Robin Thellend4c5266e2014-10-27 13:19:29 -070021 logsimpl "veyron.io/veyron/veyron/services/mgmt/logreader/impl"
Jiri Simsa24e87aa2014-06-09 09:27:34 -070022
Jiri Simsa519c5072014-09-17 21:37:57 -070023 "veyron.io/veyron/veyron2/ipc"
Robin Thellend4c5266e2014-10-27 13:19:29 -070024 "veyron.io/veyron/veyron2/naming"
Jiri Simsa519c5072014-09-17 21:37:57 -070025 "veyron.io/veyron/veyron2/security"
Bogdan Capritaa456f472014-12-10 10:18:03 -080026 "veyron.io/veyron/veyron2/services/mgmt/device"
Robin Thellendb9dd9bb2014-10-29 13:54:08 -070027 "veyron.io/veyron/veyron2/services/mgmt/pprof"
28 "veyron.io/veyron/veyron2/services/mgmt/stats"
Jiri Simsa519c5072014-09-17 21:37:57 -070029 "veyron.io/veyron/veyron2/services/security/access"
30 "veyron.io/veyron/veyron2/verror"
Todd Wang34ed4c62014-11-26 15:15:52 -080031 "veyron.io/veyron/veyron2/verror2"
Jiri Simsa519c5072014-09-17 21:37:57 -070032 "veyron.io/veyron/veyron2/vlog"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070033)
34
Bogdan Caprita2b219362014-12-09 17:03:33 -080035// internalState wraps state shared between different device manager
Bogdan Caprita4d67c042014-08-19 10:41:19 -070036// invocations.
37type internalState struct {
Bogdan Caprita7f491672014-11-13 14:51:08 -080038 callback *callbackState
39 updating *updatingState
40 securityAgent *securityAgentState
Bogdan Caprita4d67c042014-08-19 10:41:19 -070041}
42
Robert Kroegeracc778b2014-11-03 17:17:21 -080043// aclLocks provides a mutex lock for each acl file path.
44type aclLocks map[string]*sync.Mutex
45
Bogdan Caprita2b219362014-12-09 17:03:33 -080046// dispatcher holds the state of the device manager dispatcher.
Jiri Simsa5293dcb2014-05-10 09:56:38 -070047type dispatcher struct {
Gautham6fe61e52014-09-16 13:58:17 -070048 // acl/auth hold the acl and authorizer used to authorize access to the
Bogdan Caprita2b219362014-12-09 17:03:33 -080049 // device manager methods.
Asim Shankar68885192014-11-26 12:48:35 -080050 acl access.TaggedACLMap
Bogdan Caprita4d67c042014-08-19 10:41:19 -070051 auth security.Authorizer
Gautham6fe61e52014-09-16 13:58:17 -070052 // etag holds the version string for the ACL. We use this for optimistic
Bogdan Caprita2b219362014-12-09 17:03:33 -080053 // concurrency control when clients update the ACLs for the device
54 // manager.
Gautham6fe61e52014-09-16 13:58:17 -070055 etag string
Bogdan Caprita4d67c042014-08-19 10:41:19 -070056 // internal holds the state that persists across RPC method invocations.
Bogdan Capritac87a9142014-07-21 10:38:13 -070057 internal *internalState
Bogdan Caprita2b219362014-12-09 17:03:33 -080058 // config holds the device manager's (immutable) configuration state.
Bogdan Caprita4d67c042014-08-19 10:41:19 -070059 config *config.State
Gautham6fe61e52014-09-16 13:58:17 -070060 // dispatcherMutex is a lock for coordinating concurrent access to some
61 // dispatcher methods.
Bogdan Caprita7f491672014-11-13 14:51:08 -080062 mu sync.RWMutex
63 // TODO(rjkroege): Consider moving this inside internal.
Robert Kroeger1cb4a0d2014-10-20 11:55:38 -070064 uat BlessingSystemAssociationStore
Robert Kroegeracc778b2014-11-03 17:17:21 -080065 // TODO(rjkroege): Eliminate need for locks.
66 locks aclLocks
Jiri Simsa5293dcb2014-05-10 09:56:38 -070067}
68
Benjamin Prosnitzfdfbf7b2014-10-08 09:47:21 -070069var _ ipc.Dispatcher = (*dispatcher)(nil)
70
Bogdan Caprita4d67c042014-08-19 10:41:19 -070071const (
72 appsSuffix = "apps"
Bogdan Caprita9c4aa222014-12-10 14:46:30 -080073 deviceSuffix = "device"
Bogdan Caprita4d67c042014-08-19 10:41:19 -070074 configSuffix = "cfg"
Todd Wang34ed4c62014-11-26 15:15:52 -080075
Bogdan Capritaa456f472014-12-10 10:18:03 -080076 pkgPath = "veyron.io/veyron/veyron/services/mgmt/device/impl"
Bogdan Caprita4d67c042014-08-19 10:41:19 -070077)
78
79var (
Todd Wang34ed4c62014-11-26 15:15:52 -080080 ErrInvalidSuffix = verror2.Register(pkgPath+".InvalidSuffix", verror2.NoRetry, "")
81 ErrOperationFailed = verror2.Register(pkgPath+".OperationFailed", verror2.NoRetry, "")
82 ErrOperationInProgress = verror2.Register(pkgPath+".OperationInProgress", verror2.NoRetry, "")
83 ErrAppTitleMismatch = verror2.Register(pkgPath+".AppTitleMismatch", verror2.NoRetry, "")
84 ErrUpdateNoOp = verror2.Register(pkgPath+".UpdateNoOp", verror2.NoRetry, "")
85 ErrObjectNoExist = verror2.Register(pkgPath+".ObjectNoExist", verror2.NoRetry, "")
86 ErrInvalidOperation = verror2.Register(pkgPath+".InvalidOperation", verror2.NoRetry, "")
87 ErrInvalidBlessing = verror2.Register(pkgPath+".InvalidBlessing", verror2.NoRetry, "")
Bogdan Caprita4d67c042014-08-19 10:41:19 -070088)
89
Bogdan Caprita2b219362014-12-09 17:03:33 -080090// NewDispatcher is the device manager dispatcher factory.
Matt Rosencrantz5180d162014-12-03 13:48:40 -080091func NewDispatcher(principal security.Principal, config *config.State) (*dispatcher, error) {
Bogdan Capritac87a9142014-07-21 10:38:13 -070092 if err := config.Validate(); err != nil {
Gautham6fe61e52014-09-16 13:58:17 -070093 return nil, fmt.Errorf("invalid config %v: %v", config, err)
Bogdan Capritac87a9142014-07-21 10:38:13 -070094 }
Robert Kroeger1cb4a0d2014-10-20 11:55:38 -070095 uat, err := NewBlessingSystemAssociationStore(config.Root)
96 if err != nil {
97 return nil, fmt.Errorf("cannot create persistent store for identity to system account associations: %v", err)
98 }
Gautham82bb9952014-08-28 14:11:51 -070099 d := &dispatcher{
Gautham6fe61e52014-09-16 13:58:17 -0700100 etag: "default",
Bogdan Capritac87a9142014-07-21 10:38:13 -0700101 internal: &internalState{
Bogdan Caprita78b62162014-08-21 15:35:08 -0700102 callback: newCallbackState(config.Name),
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700103 updating: newUpdatingState(),
Jiri Simsa70c32052014-06-18 11:38:21 -0700104 },
Bogdan Capritac87a9142014-07-21 10:38:13 -0700105 config: config,
Robert Kroeger1cb4a0d2014-10-20 11:55:38 -0700106 uat: uat,
Robert Kroegeracc778b2014-11-03 17:17:21 -0800107 locks: make(aclLocks),
Gautham82bb9952014-08-28 14:11:51 -0700108 }
Gautham6fe61e52014-09-16 13:58:17 -0700109 // If there exists a signed ACL from a previous instance we prefer that.
110 aclFile, sigFile, _ := d.getACLFilePaths()
111 if _, err := os.Stat(aclFile); err == nil {
112 perm := os.FileMode(0700)
113 data, err := os.OpenFile(aclFile, os.O_RDONLY, perm)
114 if err != nil {
115 return nil, fmt.Errorf("failed to open acl file:%v", err)
Gautham82bb9952014-08-28 14:11:51 -0700116 }
Gautham82bb9952014-08-28 14:11:51 -0700117 defer data.Close()
Gautham6fe61e52014-09-16 13:58:17 -0700118 sig, err := os.OpenFile(sigFile, os.O_RDONLY, perm)
119 if err != nil {
120 return nil, fmt.Errorf("failed to open signature file:%v", err)
121 }
Gautham82bb9952014-08-28 14:11:51 -0700122 defer sig.Close()
Gautham6fe61e52014-09-16 13:58:17 -0700123 // read and verify the signature of the acl file
Matt Rosencrantz5180d162014-12-03 13:48:40 -0800124 reader, err := serialization.NewVerifyingReader(data, sig, principal.PublicKey())
Gautham82bb9952014-08-28 14:11:51 -0700125 if err != nil {
Bogdan Caprita2b219362014-12-09 17:03:33 -0800126 return nil, fmt.Errorf("failed to read devicemanager ACL file:%v", err)
Gautham82bb9952014-08-28 14:11:51 -0700127 }
Asim Shankar68885192014-11-26 12:48:35 -0800128 acl, err := access.ReadTaggedACLMap(reader)
Gautham82bb9952014-08-28 14:11:51 -0700129 if err != nil {
Bogdan Caprita2b219362014-12-09 17:03:33 -0800130 return nil, fmt.Errorf("failed to load devicemanager ACL:%v", err)
Gautham82bb9952014-08-28 14:11:51 -0700131 }
Matt Rosencrantz5180d162014-12-03 13:48:40 -0800132 if err := d.setACL(principal, acl, d.etag, false /* just update etag */); err != nil {
Gautham6fe61e52014-09-16 13:58:17 -0700133 return nil, err
134 }
135 } else {
136 if d.auth = vflag.NewAuthorizerOrDie(); d.auth == nil {
Bogdan Caprita2b219362014-12-09 17:03:33 -0800137 // If there are no specified ACLs we grant devicemanager
138 // access to all principals until it is claimed.
Asim Shankar68885192014-11-26 12:48:35 -0800139 d.auth = allowEveryone{}
Gautham6fe61e52014-09-16 13:58:17 -0700140 }
Gautham82bb9952014-08-28 14:11:51 -0700141 }
Bogdan Caprita7f491672014-11-13 14:51:08 -0800142 // If we're in 'security agent mode', set up the key manager agent.
143 if len(os.Getenv(agent.FdVarName)) > 0 {
144 if keyMgrAgent, err := keymgr.NewAgent(); err != nil {
145 return nil, fmt.Errorf("NewAgent() failed: %v", err)
146 } else {
147 d.internal.securityAgent = &securityAgentState{
148 keyMgrAgent: keyMgrAgent,
149 }
150 }
151 }
Gautham82bb9952014-08-28 14:11:51 -0700152 return d, nil
153}
154
Bogdan Caprita2b219362014-12-09 17:03:33 -0800155func (d *dispatcher) getACLFilePaths() (acl, signature, devicedata string) {
156 devicedata = filepath.Join(d.config.Root, "device-manager", "device-data")
157 acl, signature = filepath.Join(devicedata, "acl.devicemanager"), filepath.Join(devicedata, "acl.signature")
Gautham82bb9952014-08-28 14:11:51 -0700158 return
159}
160
Robin Thellend888f8cf2014-12-15 16:19:10 -0800161func (d *dispatcher) claimDeviceManager(ctx ipc.ServerContext) error {
Robert Kroegeracc778b2014-11-03 17:17:21 -0800162 // TODO(rjkroege): Scrub the state tree of installation and instance ACL files.
Robin Thellend888f8cf2014-12-15 16:19:10 -0800163
164 // Get the blessings to be used by the claimant.
165 blessings := ctx.Blessings()
166 if blessings == nil {
167 return verror2.Make(ErrInvalidBlessing, ctx)
168 }
169 principal := ctx.LocalPrincipal()
170 if err := principal.AddToRoots(blessings); err != nil {
171 vlog.Errorf("principal.AddToRoots(%s) failed: %v", blessings, err)
172 return verror2.Make(ErrInvalidBlessing, ctx)
173 }
174 names := blessings.ForContext(ctx)
Asim Shankar8f05c222014-10-06 22:08:19 -0700175 if len(names) == 0 {
Robin Thellend888f8cf2014-12-15 16:19:10 -0800176 vlog.Errorf("No names for claimer(%v) are trusted", blessings)
Todd Wang34ed4c62014-11-26 15:15:52 -0800177 return verror2.Make(ErrOperationFailed, nil)
Gautham82bb9952014-08-28 14:11:51 -0700178 }
Robin Thellend888f8cf2014-12-15 16:19:10 -0800179 principal.BlessingStore().Set(blessings, security.AllPrincipals)
180 principal.BlessingStore().SetDefault(blessings)
Bogdan Caprita2b219362014-12-09 17:03:33 -0800181 // Create ACLs to transfer devicemanager permissions to the provided identity.
Asim Shankar68885192014-11-26 12:48:35 -0800182 acl := make(access.TaggedACLMap)
183 for _, n := range names {
184 for _, tag := range access.AllTypicalTags() {
185 acl.Add(security.BlessingPattern(n), string(tag))
186 }
Gautham82bb9952014-08-28 14:11:51 -0700187 }
Gautham6fe61e52014-09-16 13:58:17 -0700188 _, etag, err := d.getACL()
Gautham82bb9952014-08-28 14:11:51 -0700189 if err != nil {
Gautham6fe61e52014-09-16 13:58:17 -0700190 vlog.Errorf("Failed to getACL:%v", err)
Todd Wang34ed4c62014-11-26 15:15:52 -0800191 return verror2.Make(ErrOperationFailed, nil)
Gautham82bb9952014-08-28 14:11:51 -0700192 }
Matt Rosencrantz5180d162014-12-03 13:48:40 -0800193 if err := d.setACL(principal, acl, etag, true /* store ACL on disk */); err != nil {
Robert Kroegeracc778b2014-11-03 17:17:21 -0800194 vlog.Errorf("Failed to setACL:%v", err)
Todd Wang34ed4c62014-11-26 15:15:52 -0800195 return verror2.Make(ErrOperationFailed, nil)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800196 }
197 return nil
198}
199
200// TODO(rjkroege): Further refactor ACL-setting code.
Matt Rosencrantz5180d162014-12-03 13:48:40 -0800201func setAppACL(principal security.Principal, locks aclLocks, dir string, acl access.TaggedACLMap, etag string) error {
Robert Kroegeracc778b2014-11-03 17:17:21 -0800202 aclpath := path.Join(dir, "acls", "data")
203 sigpath := path.Join(dir, "acls", "signature")
204
205 // Acquire lock. Locks are per path to an acls file.
Robert Kroeger7d979d82014-11-07 17:24:35 -0800206 lck, contains := locks[dir]
Robert Kroegeracc778b2014-11-03 17:17:21 -0800207 if !contains {
208 lck = new(sync.Mutex)
Robert Kroeger7d979d82014-11-07 17:24:35 -0800209 locks[dir] = lck
Robert Kroegeracc778b2014-11-03 17:17:21 -0800210 }
211 lck.Lock()
212 defer lck.Unlock()
213
214 f, err := os.Open(aclpath)
215 if err != nil {
216 vlog.Errorf("LoadACL(%s) failed: %v", aclpath, err)
217 return err
218 }
219 defer f.Close()
220
Asim Shankar68885192014-11-26 12:48:35 -0800221 curACL, err := access.ReadTaggedACLMap(f)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800222 if err != nil {
Asim Shankar68885192014-11-26 12:48:35 -0800223 vlog.Errorf("ReadTaggedACLMap(%s) failed: %v", aclpath, err)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800224 return err
225 }
226 curEtag, err := computeEtag(curACL)
227 if err != nil {
228 vlog.Errorf("computeEtag failed: %v", err)
229 return err
230 }
231
232 if len(etag) > 0 && etag != curEtag {
233 return verror.Make(access.ErrBadEtag, fmt.Sprintf("etag mismatch in:%s vers:%s", etag, curEtag))
234 }
235
Matt Rosencrantz5180d162014-12-03 13:48:40 -0800236 return writeACLs(principal, aclpath, sigpath, dir, acl)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800237}
238
Asim Shankar68885192014-11-26 12:48:35 -0800239func getAppACL(locks aclLocks, dir string) (access.TaggedACLMap, string, error) {
Robert Kroegeracc778b2014-11-03 17:17:21 -0800240 aclpath := path.Join(dir, "acls", "data")
241
242 // Acquire lock. Locks are per path to an acls file.
Robert Kroeger7d979d82014-11-07 17:24:35 -0800243 lck, contains := locks[dir]
Robert Kroegeracc778b2014-11-03 17:17:21 -0800244 if !contains {
245 lck = new(sync.Mutex)
Robert Kroeger7d979d82014-11-07 17:24:35 -0800246 locks[dir] = lck
Robert Kroegeracc778b2014-11-03 17:17:21 -0800247 }
248 lck.Lock()
249 defer lck.Unlock()
250
251 f, err := os.Open(aclpath)
252 if err != nil {
Asim Shankar68885192014-11-26 12:48:35 -0800253 vlog.Errorf("Open(%s) failed: %v", aclpath, err)
254 return nil, "", err
Robert Kroegeracc778b2014-11-03 17:17:21 -0800255 }
256 defer f.Close()
257
Asim Shankar68885192014-11-26 12:48:35 -0800258 acl, err := access.ReadTaggedACLMap(f)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800259 if err != nil {
Asim Shankar68885192014-11-26 12:48:35 -0800260 vlog.Errorf("ReadTaggedACLMap(%s) failed: %v", aclpath, err)
261 return nil, "", err
Robert Kroegeracc778b2014-11-03 17:17:21 -0800262 }
263 curEtag, err := computeEtag(acl)
264 if err != nil {
Asim Shankar68885192014-11-26 12:48:35 -0800265 return nil, "", err
Robert Kroegeracc778b2014-11-03 17:17:21 -0800266 }
267 return acl, curEtag, nil
268}
269
Asim Shankar68885192014-11-26 12:48:35 -0800270func computeEtag(acl access.TaggedACLMap) (string, error) {
Robert Kroegeracc778b2014-11-03 17:17:21 -0800271 b := new(bytes.Buffer)
Asim Shankar68885192014-11-26 12:48:35 -0800272 if err := acl.WriteTo(b); err != nil {
Robert Kroegeracc778b2014-11-03 17:17:21 -0800273 vlog.Errorf("Failed to save ACL:%v", err)
274 return "", err
275 }
276 // Update the acl/etag/authorizer for this dispatcher
277 md5hash := md5.Sum(b.Bytes())
278 etag := hex.EncodeToString(md5hash[:])
279 return etag, nil
280}
281
Matt Rosencrantz5180d162014-12-03 13:48:40 -0800282func writeACLs(principal security.Principal, aclFile, sigFile, dir string, acl access.TaggedACLMap) error {
Robert Kroegeracc778b2014-11-03 17:17:21 -0800283 // Create dir directory if it does not exist
284 os.MkdirAll(dir, os.FileMode(0700))
285 // Save the object to temporary data and signature files, and then move
286 // those files to the actual data and signature file.
287 data, err := ioutil.TempFile(dir, "data")
288 if err != nil {
289 vlog.Errorf("Failed to open tmpfile data:%v", err)
Todd Wang34ed4c62014-11-26 15:15:52 -0800290 return verror2.Make(ErrOperationFailed, nil)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800291 }
292 defer os.Remove(data.Name())
293 sig, err := ioutil.TempFile(dir, "sig")
294 if err != nil {
295 vlog.Errorf("Failed to open tmpfile sig:%v", err)
Todd Wang34ed4c62014-11-26 15:15:52 -0800296 return verror2.Make(ErrOperationFailed, nil)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800297 }
298 defer os.Remove(sig.Name())
Matt Rosencrantz5180d162014-12-03 13:48:40 -0800299 writer, err := serialization.NewSigningWriteCloser(data, sig, principal, nil)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800300 if err != nil {
301 vlog.Errorf("Failed to create NewSigningWriteCloser:%v", err)
Todd Wang34ed4c62014-11-26 15:15:52 -0800302 return verror2.Make(ErrOperationFailed, nil)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800303 }
Asim Shankar68885192014-11-26 12:48:35 -0800304 if err = acl.WriteTo(writer); err != nil {
Robert Kroegeracc778b2014-11-03 17:17:21 -0800305 vlog.Errorf("Failed to SaveACL:%v", err)
Todd Wang34ed4c62014-11-26 15:15:52 -0800306 return verror2.Make(ErrOperationFailed, nil)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800307 }
308 if err = writer.Close(); err != nil {
309 vlog.Errorf("Failed to Close() SigningWriteCloser:%v", err)
Todd Wang34ed4c62014-11-26 15:15:52 -0800310 return verror2.Make(ErrOperationFailed, nil)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800311 }
312 if err := os.Rename(data.Name(), aclFile); err != nil {
313 return err
314 }
315 if err := os.Rename(sig.Name(), sigFile); err != nil {
316 return err
317 }
318 return nil
Gautham6fe61e52014-09-16 13:58:17 -0700319}
320
Matt Rosencrantz5180d162014-12-03 13:48:40 -0800321func (d *dispatcher) setACL(principal security.Principal, acl access.TaggedACLMap, etag string, writeToFile bool) error {
Gautham6fe61e52014-09-16 13:58:17 -0700322 d.mu.Lock()
323 defer d.mu.Unlock()
Bogdan Caprita2b219362014-12-09 17:03:33 -0800324 aclFile, sigFile, devicedata := d.getACLFilePaths()
Robert Kroegeracc778b2014-11-03 17:17:21 -0800325
Gautham6fe61e52014-09-16 13:58:17 -0700326 if len(etag) > 0 && etag != d.etag {
327 return verror.Make(access.ErrBadEtag, fmt.Sprintf("etag mismatch in:%s vers:%s", etag, d.etag))
328 }
329 if writeToFile {
Bogdan Caprita2b219362014-12-09 17:03:33 -0800330 if err := writeACLs(principal, aclFile, sigFile, devicedata, acl); err != nil {
Gautham6fe61e52014-09-16 13:58:17 -0700331 return err
332 }
333 }
Robert Kroegeracc778b2014-11-03 17:17:21 -0800334
335 etag, err := computeEtag(acl)
336 if err != nil {
337 return err
Gautham82bb9952014-08-28 14:11:51 -0700338 }
Asim Shankar68885192014-11-26 12:48:35 -0800339 auth, err := access.TaggedACLAuthorizer(acl, access.TypicalTagType())
340 if err != nil {
341 return err
342 }
343 d.acl, d.etag, d.auth = acl, etag, auth
Gautham82bb9952014-08-28 14:11:51 -0700344 return nil
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700345}
346
Asim Shankar68885192014-11-26 12:48:35 -0800347func (d *dispatcher) getACL() (acl access.TaggedACLMap, etag string, err error) {
Gautham6fe61e52014-09-16 13:58:17 -0700348 d.mu.RLock()
349 defer d.mu.RUnlock()
350 return d.acl, d.etag, nil
351}
352
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700353// DISPATCHER INTERFACE IMPLEMENTATION
Robin Thellenda02fe8f2014-11-19 09:58:29 -0800354func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700355 components := strings.Split(suffix, "/")
356 for i := 0; i < len(components); i++ {
357 if len(components[i]) == 0 {
358 components = append(components[:i], components[i+1:]...)
359 i--
360 }
361 }
362 if len(components) == 0 {
Bogdan Caprita2b219362014-12-09 17:03:33 -0800363 return ipc.ChildrenGlobberInvoker(deviceSuffix, appsSuffix), d.auth, nil
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700364 }
Bogdan Caprita2b219362014-12-09 17:03:33 -0800365 // The implementation of the device manager is split up into several
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700366 // invokers, which are instantiated depending on the receiver name
367 // prefix.
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700368 switch components[0] {
Bogdan Caprita2b219362014-12-09 17:03:33 -0800369 case deviceSuffix:
Bogdan Capritaa456f472014-12-10 10:18:03 -0800370 receiver := device.DeviceServer(&deviceService{
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700371 callback: d.internal.callback,
372 updating: d.internal.updating,
373 config: d.config,
Gautham82bb9952014-08-28 14:11:51 -0700374 disp: d,
Robert Kroeger362ff892014-09-29 14:23:47 -0700375 uat: d.uat,
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700376 })
Cosmos Nicolaou710daa22014-11-11 19:39:18 -0800377 return receiver, d.auth, nil
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700378 case appsSuffix:
Robin Thellend8a0f04f2014-11-17 10:40:24 -0800379 // Requests to apps/*/*/*/logs are handled locally by LogFileService.
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700380 // Requests to apps/*/*/*/pprof are proxied to the apps' __debug/pprof object.
381 // Requests to apps/*/*/*/stats are proxied to the apps' __debug/stats object.
Robin Thellend9bc8fcb2014-11-17 10:23:04 -0800382 // Everything else is handled by the Application server.
Robin Thellendac7128c2014-11-11 09:58:28 -0800383 if len(components) >= 5 {
Robin Thellend4c5266e2014-10-27 13:19:29 -0700384 appInstanceDir, err := instanceDir(d.config.Root, components[1:4])
385 if err != nil {
386 return nil, nil, err
387 }
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700388 switch kind := components[4]; kind {
389 case "logs":
390 logsDir := filepath.Join(appInstanceDir, "logs")
391 suffix := naming.Join(components[5:]...)
Robin Thellend8a0f04f2014-11-17 10:40:24 -0800392 return logsimpl.NewLogFileService(logsDir, suffix), d.auth, nil
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700393 case "pprof", "stats":
394 info, err := loadInstanceInfo(appInstanceDir)
395 if err != nil {
396 return nil, nil, err
397 }
398 if !instanceStateIs(appInstanceDir, started) {
Todd Wang34ed4c62014-11-26 15:15:52 -0800399 return nil, nil, verror2.Make(ErrInvalidSuffix, nil)
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700400 }
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700401 var sigStub signatureStub
402 if kind == "pprof" {
Todd Wang702385a2014-11-07 01:54:08 -0800403 sigStub = pprof.PProfServer(nil)
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700404 } else {
Todd Wang702385a2014-11-07 01:54:08 -0800405 sigStub = stats.StatsServer(nil)
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700406 }
407 suffix := naming.Join("__debug", naming.Join(components[4:]...))
408 remote := naming.JoinAddressName(info.AppCycleMgrName, suffix)
Matt Rosencrantz5180d162014-12-03 13:48:40 -0800409 invoker := &proxyInvoker{
410 remote: remote,
411 access: access.Debug,
412 sigStub: sigStub,
413 }
414 return invoker, d.auth, nil
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700415 }
Robin Thellend4c5266e2014-10-27 13:19:29 -0700416 }
Bogdan Caprita2b219362014-12-09 17:03:33 -0800417 deviceACLs, _, err := d.getACL()
Robert Kroegeracc778b2014-11-03 17:17:21 -0800418 if err != nil {
419 return nil, nil, err
420 }
Bogdan Capritaa456f472014-12-10 10:18:03 -0800421 receiver := device.ApplicationServer(&appService{
Bogdan Caprita7f491672014-11-13 14:51:08 -0800422 callback: d.internal.callback,
423 config: d.config,
424 suffix: components[1:],
425 uat: d.uat,
426 locks: d.locks,
Bogdan Caprita2b219362014-12-09 17:03:33 -0800427 deviceACL: deviceACLs,
Bogdan Caprita7f491672014-11-13 14:51:08 -0800428 securityAgent: d.internal.securityAgent,
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700429 })
Robert Kroegeracc778b2014-11-03 17:17:21 -0800430 appSpecificAuthorizer, err := newAppSpecificAuthorizer(d.auth, d.config, components[1:])
431 if err != nil {
432 return nil, nil, err
433 }
Cosmos Nicolaou710daa22014-11-11 19:39:18 -0800434 return receiver, appSpecificAuthorizer, nil
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700435 case configSuffix:
436 if len(components) != 2 {
Todd Wang34ed4c62014-11-26 15:15:52 -0800437 return nil, nil, verror2.Make(ErrInvalidSuffix, nil)
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700438 }
Bogdan Capritaa456f472014-12-10 10:18:03 -0800439 receiver := idevice.ConfigServer(&configService{
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700440 callback: d.internal.callback,
441 suffix: components[1],
442 })
Bogdan Capritab9501d12014-10-10 15:02:03 -0700443 // The nil authorizer ensures that only principals blessed by
Bogdan Caprita2b219362014-12-09 17:03:33 -0800444 // the device manager can talk back to it. All apps started by
445 // the device manager should fall in that category.
Bogdan Capritab9501d12014-10-10 15:02:03 -0700446 //
447 // TODO(caprita,rjkroege): We should further refine this, by
448 // only allowing the app to update state referring to itself
449 // (and not other apps).
Cosmos Nicolaou710daa22014-11-11 19:39:18 -0800450 return receiver, nil, nil
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700451 default:
Todd Wang34ed4c62014-11-26 15:15:52 -0800452 return nil, nil, verror2.Make(ErrInvalidSuffix, nil)
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700453 }
Jiri Simsa24e87aa2014-06-09 09:27:34 -0700454}
Robert Kroegeracc778b2014-11-03 17:17:21 -0800455
456func newAppSpecificAuthorizer(sec security.Authorizer, config *config.State, suffix []string) (security.Authorizer, error) {
Bogdan Caprita2b219362014-12-09 17:03:33 -0800457 // TODO(rjkroege): This does not support <appname>.Start() to start all
458 // instances. Correct this.
Robert Kroegeracc778b2014-11-03 17:17:21 -0800459
Bogdan Caprita2b219362014-12-09 17:03:33 -0800460 // If we are attempting a method invocation against "apps/", we use the
461 // device-manager wide ACL.
Robert Kroegeracc778b2014-11-03 17:17:21 -0800462 if len(suffix) == 0 || len(suffix) == 1 {
463 return sec, nil
464 }
465 // Otherwise, we require a per-installation and per-instance ACL file.
Robert Kroegeracc778b2014-11-03 17:17:21 -0800466 if len(suffix) == 2 {
467 p, err := installationDirCore(suffix, config.Root)
468 if err != nil {
469 vlog.Errorf("newAppSpecificAuthorizer failed: %v", err)
470 return nil, err
471 }
Asim Shankar68885192014-11-26 12:48:35 -0800472 return access.TaggedACLAuthorizerFromFile(path.Join(p, "acls", "data"), access.TypicalTagType())
473 }
474 if len(suffix) > 2 {
Robert Kroegeracc778b2014-11-03 17:17:21 -0800475 p, err := instanceDir(config.Root, suffix[0:3])
476 if err != nil {
477 vlog.Errorf("newAppSpecificAuthorizer failed: %v", err)
478 return nil, err
479 }
Asim Shankar68885192014-11-26 12:48:35 -0800480 return access.TaggedACLAuthorizerFromFile(path.Join(p, "acls", "data"), access.TypicalTagType())
Robert Kroegeracc778b2014-11-03 17:17:21 -0800481 }
Todd Wang34ed4c62014-11-26 15:15:52 -0800482 return nil, verror2.Make(ErrInvalidSuffix, nil)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800483}
Asim Shankar68885192014-11-26 12:48:35 -0800484
485// allowEveryone implements the authorization policy that allows all principals
486// access.
487type allowEveryone struct{}
488
Robin Thellend888f8cf2014-12-15 16:19:10 -0800489func (allowEveryone) Authorize(ctx security.Context) error {
490 vlog.Infof("Device manager is unclaimed. Allow %q.%s() from %q.", ctx.Suffix(), ctx.Method(), ctx.RemoteBlessings())
491 return nil
492}