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 | |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 5 | package impl |
| 6 | |
| 7 | import ( |
Bogdan Caprita | c87a914 | 2014-07-21 10:38:13 -0700 | [diff] [blame] | 8 | "fmt" |
Gautham | 82bb995 | 2014-08-28 14:11:51 -0700 | [diff] [blame] | 9 | "os" |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 10 | "path" |
Gautham | 82bb995 | 2014-08-28 14:11:51 -0700 | [diff] [blame] | 11 | "path/filepath" |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 12 | "strings" |
Gautham | 6fe61e5 | 2014-09-16 13:58:17 -0700 | [diff] [blame] | 13 | "sync" |
Jiri Simsa | 24e87aa | 2014-06-09 09:27:34 -0700 | [diff] [blame] | 14 | |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 15 | "v.io/v23" |
| 16 | "v.io/v23/context" |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 17 | "v.io/v23/naming" |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 18 | "v.io/v23/rpc" |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 19 | "v.io/v23/security" |
Todd Wang | 387d8a4 | 2015-03-30 17:09:05 -0700 | [diff] [blame] | 20 | "v.io/v23/security/access" |
Todd Wang | 94c9d0b | 2015-04-01 14:27:00 -0700 | [diff] [blame] | 21 | "v.io/v23/services/device" |
| 22 | "v.io/v23/services/pprof" |
| 23 | "v.io/v23/services/stats" |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 24 | "v.io/v23/vdl" |
| 25 | "v.io/v23/vdlroot/signature" |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 26 | "v.io/v23/verror" |
Jiri Simsa | 337af23 | 2015-02-27 14:36:46 -0800 | [diff] [blame] | 27 | "v.io/x/lib/vlog" |
Todd Wang | 8123b5e | 2015-05-14 18:44:43 -0700 | [diff] [blame] | 28 | "v.io/x/ref" |
Todd Wang | b351149 | 2015-04-07 23:32:34 -0700 | [diff] [blame] | 29 | "v.io/x/ref/services/agent/keymgr" |
| 30 | s_device "v.io/x/ref/services/device" |
| 31 | "v.io/x/ref/services/device/internal/config" |
Todd Wang | fb93903 | 2015-04-08 16:42:44 -0700 | [diff] [blame] | 32 | "v.io/x/ref/services/internal/logreaderlib" |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 33 | "v.io/x/ref/services/internal/pathperms" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 34 | ) |
| 35 | |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 36 | // internalState wraps state shared between different device manager |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 37 | // invocations. |
| 38 | type internalState struct { |
Bogdan Caprita | 29a3b35 | 2015-01-16 16:28:49 -0800 | [diff] [blame] | 39 | callback *callbackState |
| 40 | updating *updatingState |
| 41 | securityAgent *securityAgentState |
| 42 | restartHandler func() |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 43 | testMode bool |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 44 | } |
| 45 | |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 46 | // dispatcher holds the state of the device manager dispatcher. |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 47 | type dispatcher struct { |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 48 | // internal holds the state that persists across RPC method invocations. |
Bogdan Caprita | c87a914 | 2014-07-21 10:38:13 -0700 | [diff] [blame] | 49 | internal *internalState |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 50 | // config holds the device manager's (immutable) configuration state. |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 51 | config *config.State |
Gautham | 6fe61e5 | 2014-09-16 13:58:17 -0700 | [diff] [blame] | 52 | // dispatcherMutex is a lock for coordinating concurrent access to some |
| 53 | // dispatcher methods. |
Bogdan Caprita | 7f49167 | 2014-11-13 14:51:08 -0800 | [diff] [blame] | 54 | mu sync.RWMutex |
| 55 | // TODO(rjkroege): Consider moving this inside internal. |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 56 | uat BlessingSystemAssociationStore |
| 57 | permsStore *pathperms.PathStore |
Robin Thellend | acaa432 | 2015-02-05 11:00:28 -0800 | [diff] [blame] | 58 | // Namespace |
| 59 | mtAddress string // The address of the local mounttable. |
Robert Kroeger | 936853a | 2015-01-28 17:42:55 -0800 | [diff] [blame] | 60 | // reap is the app process monitoring subsystem. |
| 61 | reap reaper |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 62 | } |
| 63 | |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 64 | var _ rpc.Dispatcher = (*dispatcher)(nil) |
Benjamin Prosnitz | fdfbf7b | 2014-10-08 09:47:21 -0700 | [diff] [blame] | 65 | |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 66 | const ( |
| 67 | appsSuffix = "apps" |
Bogdan Caprita | 9c4aa22 | 2014-12-10 14:46:30 -0800 | [diff] [blame] | 68 | deviceSuffix = "device" |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 69 | configSuffix = "cfg" |
Todd Wang | 34ed4c6 | 2014-11-26 15:15:52 -0800 | [diff] [blame] | 70 | |
Todd Wang | cd4b3cc | 2015-04-06 16:42:02 -0700 | [diff] [blame] | 71 | pkgPath = "v.io/x/ref/services/device/internal/impl" |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 72 | ) |
| 73 | |
| 74 | var ( |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 75 | ErrInvalidSuffix = verror.Register(pkgPath+".InvalidSuffix", verror.NoRetry, "{1:}{2:} invalid suffix{:_}") |
| 76 | ErrOperationFailed = verror.Register(pkgPath+".OperationFailed", verror.NoRetry, "{1:}{2:} operation failed{:_}") |
| 77 | ErrOperationInProgress = verror.Register(pkgPath+".OperationInProgress", verror.NoRetry, "{1:}{2:} operation in progress{:_}") |
| 78 | ErrAppTitleMismatch = verror.Register(pkgPath+".AppTitleMismatch", verror.NoRetry, "{1:}{2:} app title mismatch{:_}") |
| 79 | ErrUpdateNoOp = verror.Register(pkgPath+".UpdateNoOp", verror.NoRetry, "{1:}{2:} update is no op{:_}") |
| 80 | ErrInvalidOperation = verror.Register(pkgPath+".InvalidOperation", verror.NoRetry, "{1:}{2:} invalid operation{:_}") |
| 81 | ErrInvalidBlessing = verror.Register(pkgPath+".InvalidBlessing", verror.NoRetry, "{1:}{2:} invalid blessing{:_}") |
| 82 | ErrInvalidPairingToken = verror.Register(pkgPath+".InvalidPairingToken", verror.NoRetry, "{1:}{2:} pairing token mismatch{:_}") |
| 83 | ErrUnclaimedDevice = verror.Register(pkgPath+".UnclaimedDevice", verror.NoRetry, "{1:}{2:} device needs to be claimed first") |
| 84 | ErrDeviceAlreadyClaimed = verror.Register(pkgPath+".AlreadyClaimed", verror.NoRetry, "{1:}{2:} device has already been claimed") |
Mike Burrows | 39bbaaf | 2015-03-24 11:27:32 -0700 | [diff] [blame] | 85 | |
| 86 | errInvalidConfig = verror.Register(pkgPath+".errInvalidConfig", verror.NoRetry, "{1:}{2:} invalid config {3}{:_}") |
| 87 | errCantCreateAccountStore = verror.Register(pkgPath+".errCantCreateAccountStore", verror.NoRetry, "{1:}{2:} cannot create persistent store for identity to system account associations{:_}") |
| 88 | errCantCreateAppWatcher = verror.Register(pkgPath+".errCantCreateAppWatcher", verror.NoRetry, "{1:}{2:} cannot create app status watcher{:_}") |
| 89 | errNewAgentFailed = verror.Register(pkgPath+".errNewAgentFailed", verror.NoRetry, "{1:}{2:} NewAgent() failed{:_}") |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 90 | ) |
| 91 | |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 92 | // NewClaimableDispatcher returns an rpc.Dispatcher that allows the device to |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 93 | // be Claimed if it hasn't been already and a channel that will be closed once |
| 94 | // the device has been claimed. |
| 95 | // |
| 96 | // It returns (nil, nil) if the device is no longer claimable. |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 97 | func NewClaimableDispatcher(ctx *context.T, config *config.State, pairingToken string) (rpc.Dispatcher, <-chan struct{}) { |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 98 | var ( |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 99 | permsDir = PermsDir(config) |
| 100 | permsStore = pathperms.NewPathStore(v23.GetPrincipal(ctx)) |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 101 | ) |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 102 | if _, _, err := permsStore.Get(permsDir); !os.IsNotExist(err) { |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 103 | return nil, nil |
| 104 | } |
| 105 | // The device is claimable only if Claim hasn't been called before. The |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 106 | // existence of the Permissions file is an indication of a successful prior |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 107 | // call to Claim. |
| 108 | notify := make(chan struct{}) |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 109 | return &claimable{token: pairingToken, permsStore: permsStore, permsDir: permsDir, notify: notify}, notify |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 110 | } |
| 111 | |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 112 | // NewDispatcher is the device manager dispatcher factory. |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 113 | func NewDispatcher(ctx *context.T, config *config.State, mtAddress string, testMode bool, restartHandler func(), permStore *pathperms.PathStore) (rpc.Dispatcher, error) { |
Bogdan Caprita | c87a914 | 2014-07-21 10:38:13 -0700 | [diff] [blame] | 114 | if err := config.Validate(); err != nil { |
Mike Burrows | 39bbaaf | 2015-03-24 11:27:32 -0700 | [diff] [blame] | 115 | return nil, verror.New(errInvalidConfig, ctx, config, err) |
Bogdan Caprita | c87a914 | 2014-07-21 10:38:13 -0700 | [diff] [blame] | 116 | } |
Robert Kroeger | 1cb4a0d | 2014-10-20 11:55:38 -0700 | [diff] [blame] | 117 | uat, err := NewBlessingSystemAssociationStore(config.Root) |
| 118 | if err != nil { |
Mike Burrows | 39bbaaf | 2015-03-24 11:27:32 -0700 | [diff] [blame] | 119 | return nil, verror.New(errCantCreateAccountStore, ctx, err) |
Robert Kroeger | 1cb4a0d | 2014-10-20 11:55:38 -0700 | [diff] [blame] | 120 | } |
Robert Kroeger | 7464517 | 2015-02-11 14:22:21 -0800 | [diff] [blame] | 121 | reap, err := newReaper(ctx, config.Root) |
| 122 | if err != nil { |
Mike Burrows | 39bbaaf | 2015-03-24 11:27:32 -0700 | [diff] [blame] | 123 | return nil, verror.New(errCantCreateAppWatcher, ctx, err) |
Robert Kroeger | 7464517 | 2015-02-11 14:22:21 -0800 | [diff] [blame] | 124 | } |
Arup Mukherjee | 746444f | 2015-04-16 19:13:24 -0700 | [diff] [blame] | 125 | initSuidHelper(config.Helper) |
Gautham | 82bb995 | 2014-08-28 14:11:51 -0700 | [diff] [blame] | 126 | d := &dispatcher{ |
Bogdan Caprita | c87a914 | 2014-07-21 10:38:13 -0700 | [diff] [blame] | 127 | internal: &internalState{ |
Bogdan Caprita | 29a3b35 | 2015-01-16 16:28:49 -0800 | [diff] [blame] | 128 | callback: newCallbackState(config.Name), |
| 129 | updating: newUpdatingState(), |
| 130 | restartHandler: restartHandler, |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 131 | testMode: testMode, |
Jiri Simsa | 70c3205 | 2014-06-18 11:38:21 -0700 | [diff] [blame] | 132 | }, |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 133 | config: config, |
| 134 | uat: uat, |
| 135 | permsStore: permStore, |
| 136 | mtAddress: mtAddress, |
| 137 | reap: reap, |
Gautham | 82bb995 | 2014-08-28 14:11:51 -0700 | [diff] [blame] | 138 | } |
Robert Kroeger | e95ed6d | 2015-01-14 17:41:04 -0800 | [diff] [blame] | 139 | |
Bogdan Caprita | 7f49167 | 2014-11-13 14:51:08 -0800 | [diff] [blame] | 140 | // If we're in 'security agent mode', set up the key manager agent. |
Todd Wang | 8123b5e | 2015-05-14 18:44:43 -0700 | [diff] [blame] | 141 | if len(os.Getenv(ref.EnvAgentEndpoint)) > 0 { |
Bogdan Caprita | 7f49167 | 2014-11-13 14:51:08 -0800 | [diff] [blame] | 142 | if keyMgrAgent, err := keymgr.NewAgent(); err != nil { |
Mike Burrows | 39bbaaf | 2015-03-24 11:27:32 -0700 | [diff] [blame] | 143 | return nil, verror.New(errNewAgentFailed, ctx, err) |
Bogdan Caprita | 7f49167 | 2014-11-13 14:51:08 -0800 | [diff] [blame] | 144 | } else { |
| 145 | d.internal.securityAgent = &securityAgentState{ |
| 146 | keyMgrAgent: keyMgrAgent, |
| 147 | } |
| 148 | } |
| 149 | } |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 150 | if testMode { |
| 151 | return &testModeDispatcher{d}, nil |
| 152 | } |
Gautham | 82bb995 | 2014-08-28 14:11:51 -0700 | [diff] [blame] | 153 | return d, nil |
| 154 | } |
| 155 | |
Robert Kroeger | 936853a | 2015-01-28 17:42:55 -0800 | [diff] [blame] | 156 | // Shutdown the dispatcher. |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 157 | func Shutdown(rpcd rpc.Dispatcher) { |
| 158 | switch d := rpcd.(type) { |
Robert Kroeger | 936853a | 2015-01-28 17:42:55 -0800 | [diff] [blame] | 159 | case *dispatcher: |
| 160 | d.reap.shutdown() |
| 161 | case *testModeDispatcher: |
| 162 | Shutdown(d.realDispatcher) |
| 163 | default: |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 164 | vlog.Panicf("%v not a supported dispatcher type.", rpcd) |
Robert Kroeger | 936853a | 2015-01-28 17:42:55 -0800 | [diff] [blame] | 165 | } |
| 166 | } |
| 167 | |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 168 | // Logging invoker that logs any error messages before returning. |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 169 | func newLoggingInvoker(obj interface{}) (rpc.Invoker, error) { |
| 170 | if invoker, ok := obj.(rpc.Invoker); ok { |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 171 | return &loggingInvoker{invoker}, nil |
| 172 | } |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 173 | invoker, err := rpc.ReflectInvoker(obj) |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 174 | if err != nil { |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 175 | vlog.Errorf("rpc.ReflectInvoker returned error: %v", err) |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 176 | return nil, err |
| 177 | } |
| 178 | return &loggingInvoker{invoker}, nil |
| 179 | } |
| 180 | |
| 181 | type loggingInvoker struct { |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 182 | invoker rpc.Invoker |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | func (l *loggingInvoker) Prepare(method string, numArgs int) (argptrs []interface{}, tags []*vdl.Value, err error) { |
| 186 | argptrs, tags, err = l.invoker.Prepare(method, numArgs) |
| 187 | if err != nil { |
| 188 | vlog.Errorf("Prepare(%s %d) returned error: %v", method, numArgs, err) |
| 189 | } |
| 190 | return |
| 191 | } |
| 192 | |
Todd Wang | 54feabe | 2015-04-15 23:38:26 -0700 | [diff] [blame] | 193 | func (l *loggingInvoker) Invoke(ctx *context.T, call rpc.StreamServerCall, method string, argptrs []interface{}) (results []interface{}, err error) { |
| 194 | results, err = l.invoker.Invoke(ctx, call, method, argptrs) |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 195 | if err != nil { |
| 196 | vlog.Errorf("Invoke(method:%s argptrs:%v) returned error: %v", method, argptrs, err) |
| 197 | } |
| 198 | return |
| 199 | } |
| 200 | |
Todd Wang | 54feabe | 2015-04-15 23:38:26 -0700 | [diff] [blame] | 201 | func (l *loggingInvoker) Signature(ctx *context.T, call rpc.ServerCall) ([]signature.Interface, error) { |
| 202 | sig, err := l.invoker.Signature(ctx, call) |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 203 | if err != nil { |
| 204 | vlog.Errorf("Signature returned error: %v", err) |
| 205 | } |
| 206 | return sig, err |
| 207 | } |
| 208 | |
Todd Wang | 54feabe | 2015-04-15 23:38:26 -0700 | [diff] [blame] | 209 | func (l *loggingInvoker) MethodSignature(ctx *context.T, call rpc.ServerCall, method string) (signature.Method, error) { |
| 210 | methodSig, err := l.invoker.MethodSignature(ctx, call, method) |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 211 | if err != nil { |
| 212 | vlog.Errorf("MethodSignature(%s) returned error: %v", method, err) |
| 213 | } |
| 214 | return methodSig, err |
| 215 | } |
| 216 | |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 217 | func (l *loggingInvoker) Globber() *rpc.GlobState { |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 218 | return l.invoker.Globber() |
| 219 | } |
| 220 | |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 221 | // DISPATCHER INTERFACE IMPLEMENTATION |
Robin Thellend | a02fe8f | 2014-11-19 09:58:29 -0800 | [diff] [blame] | 222 | func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) { |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 223 | invoker, auth, err := d.internalLookup(suffix) |
| 224 | if err != nil { |
| 225 | return nil, nil, err |
| 226 | } |
| 227 | loggingInvoker, err := newLoggingInvoker(invoker) |
| 228 | if err != nil { |
| 229 | return nil, nil, err |
| 230 | } |
| 231 | return loggingInvoker, auth, nil |
| 232 | } |
| 233 | |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 234 | func newTestableHierarchicalAuth(testMode bool, rootDir, childDir string, get pathperms.PermsGetter) (security.Authorizer, error) { |
Robert Kroeger | 7a8b222 | 2015-03-06 07:58:57 -0800 | [diff] [blame] | 235 | if testMode { |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 236 | // In test mode, the device manager will not be able to read the |
| 237 | // Permissions, because they were signed with the key of the real device |
| 238 | // manager. It's not a problem because the testModeDispatcher overrides the |
| 239 | // authorizer anyway. |
Robert Kroeger | 7a8b222 | 2015-03-06 07:58:57 -0800 | [diff] [blame] | 240 | return nil, nil |
| 241 | } |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 242 | return pathperms.NewHierarchicalAuthorizer(rootDir, childDir, get) |
Robert Kroeger | 7a8b222 | 2015-03-06 07:58:57 -0800 | [diff] [blame] | 243 | } |
| 244 | |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 245 | func (d *dispatcher) internalLookup(suffix string) (interface{}, security.Authorizer, error) { |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 246 | components := strings.Split(suffix, "/") |
| 247 | for i := 0; i < len(components); i++ { |
| 248 | if len(components[i]) == 0 { |
| 249 | components = append(components[:i], components[i+1:]...) |
| 250 | i-- |
| 251 | } |
| 252 | } |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 253 | |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 254 | // TODO(rjkroege): Permit the root Permissions to diverge for the device and |
| 255 | // app sub-namespaces of the device manager after claiming. |
| 256 | auth, err := newTestableHierarchicalAuth(d.internal.testMode, PermsDir(d.config), PermsDir(d.config), d.permsStore) |
Robert Kroeger | e95ed6d | 2015-01-14 17:41:04 -0800 | [diff] [blame] | 257 | if err != nil { |
| 258 | return nil, nil, err |
| 259 | } |
| 260 | |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 261 | if len(components) == 0 { |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 262 | return rpc.ChildrenGlobberInvoker(deviceSuffix, appsSuffix), auth, nil |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 263 | } |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 264 | // The implementation of the device manager is split up into several |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 265 | // invokers, which are instantiated depending on the receiver name |
| 266 | // prefix. |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 267 | switch components[0] { |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 268 | case deviceSuffix: |
Bogdan Caprita | a456f47 | 2014-12-10 10:18:03 -0800 | [diff] [blame] | 269 | receiver := device.DeviceServer(&deviceService{ |
Bogdan Caprita | 29a3b35 | 2015-01-16 16:28:49 -0800 | [diff] [blame] | 270 | callback: d.internal.callback, |
| 271 | updating: d.internal.updating, |
| 272 | restartHandler: d.internal.restartHandler, |
| 273 | config: d.config, |
| 274 | disp: d, |
| 275 | uat: d.uat, |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 276 | securityAgent: d.internal.securityAgent, |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 277 | }) |
Robert Kroeger | e95ed6d | 2015-01-14 17:41:04 -0800 | [diff] [blame] | 278 | return receiver, auth, nil |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 279 | case appsSuffix: |
Robin Thellend | 8a0f04f | 2014-11-17 10:40:24 -0800 | [diff] [blame] | 280 | // Requests to apps/*/*/*/logs are handled locally by LogFileService. |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 281 | // Requests to apps/*/*/*/pprof are proxied to the apps' __debug/pprof object. |
| 282 | // Requests to apps/*/*/*/stats are proxied to the apps' __debug/stats object. |
Robin Thellend | 9bc8fcb | 2014-11-17 10:23:04 -0800 | [diff] [blame] | 283 | // Everything else is handled by the Application server. |
Robin Thellend | ac7128c | 2014-11-11 09:58:28 -0800 | [diff] [blame] | 284 | if len(components) >= 5 { |
Robin Thellend | 4c5266e | 2014-10-27 13:19:29 -0700 | [diff] [blame] | 285 | appInstanceDir, err := instanceDir(d.config.Root, components[1:4]) |
| 286 | if err != nil { |
| 287 | return nil, nil, err |
| 288 | } |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 289 | switch kind := components[4]; kind { |
| 290 | case "logs": |
| 291 | logsDir := filepath.Join(appInstanceDir, "logs") |
| 292 | suffix := naming.Join(components[5:]...) |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 293 | appSpecificAuthorizer, err := newAppSpecificAuthorizer(auth, d.config, components[1:], d.permsStore) |
Robert Kroeger | 8f914be | 2015-03-14 16:32:37 -0700 | [diff] [blame] | 294 | if err != nil { |
| 295 | return nil, nil, err |
| 296 | } |
Todd Wang | 1ea8f19 | 2015-04-03 17:31:51 -0700 | [diff] [blame] | 297 | return logreaderlib.NewLogFileService(logsDir, suffix), appSpecificAuthorizer, nil |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 298 | case "pprof", "stats": |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 299 | info, err := loadInstanceInfo(nil, appInstanceDir) |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 300 | if err != nil { |
| 301 | return nil, nil, err |
| 302 | } |
Bogdan Caprita | 2b05032 | 2015-04-17 09:04:03 -0700 | [diff] [blame] | 303 | if !instanceStateIs(appInstanceDir, device.InstanceStateRunning) { |
Todd Wang | ff73e1f | 2015-02-10 21:45:52 -0800 | [diff] [blame] | 304 | return nil, nil, verror.New(ErrInvalidSuffix, nil) |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 305 | } |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 306 | var desc []rpc.InterfaceDesc |
Todd Wang | f519f8f | 2015-01-21 10:07:41 -0800 | [diff] [blame] | 307 | switch kind { |
| 308 | case "pprof": |
| 309 | desc = pprof.PProfServer(nil).Describe__() |
| 310 | case "stats": |
| 311 | desc = stats.StatsServer(nil).Describe__() |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 312 | } |
| 313 | suffix := naming.Join("__debug", naming.Join(components[4:]...)) |
| 314 | remote := naming.JoinAddressName(info.AppCycleMgrName, suffix) |
Robert Kroeger | 16ee22b | 2015-03-12 14:57:09 -0700 | [diff] [blame] | 315 | |
| 316 | // Use hierarchical auth with debugacls under debug access. |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 317 | appSpecificAuthorizer, err := newAppSpecificAuthorizer(auth, d.config, components[1:], d.permsStore) |
Robert Kroeger | 16ee22b | 2015-03-12 14:57:09 -0700 | [diff] [blame] | 318 | if err != nil { |
| 319 | return nil, nil, err |
| 320 | } |
| 321 | return newProxyInvoker(remote, access.Debug, desc), appSpecificAuthorizer, nil |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 322 | } |
Robin Thellend | 4c5266e | 2014-10-27 13:19:29 -0700 | [diff] [blame] | 323 | } |
Bogdan Caprita | a456f47 | 2014-12-10 10:18:03 -0800 | [diff] [blame] | 324 | receiver := device.ApplicationServer(&appService{ |
Robert Kroeger | 450fdf1 | 2015-05-19 14:40:42 -0700 | [diff] [blame^] | 325 | config: d.config, |
| 326 | suffix: components[1:], |
| 327 | uat: d.uat, |
| 328 | permsStore: d.permsStore, |
| 329 | reap: d.reap, |
| 330 | appStart: &appStartState{ |
| 331 | callback: d.internal.callback, |
| 332 | securityAgent: d.internal.securityAgent, |
| 333 | mtAddress: d.mtAddress, |
| 334 | }, |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 335 | }) |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 336 | appSpecificAuthorizer, err := newAppSpecificAuthorizer(auth, d.config, components[1:], d.permsStore) |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 337 | if err != nil { |
| 338 | return nil, nil, err |
| 339 | } |
Cosmos Nicolaou | 710daa2 | 2014-11-11 19:39:18 -0800 | [diff] [blame] | 340 | return receiver, appSpecificAuthorizer, nil |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 341 | case configSuffix: |
| 342 | if len(components) != 2 { |
Todd Wang | ff73e1f | 2015-02-10 21:45:52 -0800 | [diff] [blame] | 343 | return nil, nil, verror.New(ErrInvalidSuffix, nil) |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 344 | } |
Todd Wang | cd4b3cc | 2015-04-06 16:42:02 -0700 | [diff] [blame] | 345 | receiver := s_device.ConfigServer(&configService{ |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 346 | callback: d.internal.callback, |
| 347 | suffix: components[1], |
| 348 | }) |
Bogdan Caprita | b9501d1 | 2014-10-10 15:02:03 -0700 | [diff] [blame] | 349 | // The nil authorizer ensures that only principals blessed by |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 350 | // the device manager can talk back to it. All apps started by |
| 351 | // the device manager should fall in that category. |
Bogdan Caprita | b9501d1 | 2014-10-10 15:02:03 -0700 | [diff] [blame] | 352 | // |
| 353 | // TODO(caprita,rjkroege): We should further refine this, by |
| 354 | // only allowing the app to update state referring to itself |
| 355 | // (and not other apps). |
Cosmos Nicolaou | 710daa2 | 2014-11-11 19:39:18 -0800 | [diff] [blame] | 356 | return receiver, nil, nil |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 357 | default: |
Todd Wang | ff73e1f | 2015-02-10 21:45:52 -0800 | [diff] [blame] | 358 | return nil, nil, verror.New(ErrInvalidSuffix, nil) |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 359 | } |
Jiri Simsa | 24e87aa | 2014-06-09 09:27:34 -0700 | [diff] [blame] | 360 | } |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 361 | |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 362 | // testModeDispatcher is a wrapper around the real dispatcher. It returns the |
| 363 | // exact same object as the real dispatcher, but the authorizer only allows |
Bogdan Caprita | 2b05032 | 2015-04-17 09:04:03 -0700 | [diff] [blame] | 364 | // calls to "device".Delete(). |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 365 | type testModeDispatcher struct { |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 366 | realDispatcher rpc.Dispatcher |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 367 | } |
| 368 | |
| 369 | func (d *testModeDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) { |
| 370 | obj, _, err := d.realDispatcher.Lookup(suffix) |
| 371 | return obj, d, err |
| 372 | } |
| 373 | |
Todd Wang | 4264e4b | 2015-04-16 22:43:40 -0700 | [diff] [blame] | 374 | func (testModeDispatcher) Authorize(ctx *context.T, call security.Call) error { |
Bogdan Caprita | 2b05032 | 2015-04-17 09:04:03 -0700 | [diff] [blame] | 375 | if call.Suffix() == deviceSuffix && call.Method() == "Delete" { |
Matt Rosencrantz | 9dce9b2 | 2015-03-02 10:48:37 -0800 | [diff] [blame] | 376 | vlog.Infof("testModeDispatcher.Authorize: Allow %q.%s()", call.Suffix(), call.Method()) |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 377 | return nil |
| 378 | } |
Matt Rosencrantz | 9dce9b2 | 2015-03-02 10:48:37 -0800 | [diff] [blame] | 379 | vlog.Infof("testModeDispatcher.Authorize: Reject %q.%s()", call.Suffix(), call.Method()) |
Todd Wang | ff73e1f | 2015-02-10 21:45:52 -0800 | [diff] [blame] | 380 | return verror.New(ErrInvalidSuffix, nil) |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 381 | } |
| 382 | |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 383 | func newAppSpecificAuthorizer(sec security.Authorizer, config *config.State, suffix []string, getter pathperms.PermsGetter) (security.Authorizer, error) { |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 384 | // TODO(rjkroege): This does not support <appname>.Start() to start all |
| 385 | // instances. Correct this. |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 386 | |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 387 | // If we are attempting a method invocation against "apps/", we use the root |
| 388 | // Permissions. |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 389 | if len(suffix) == 0 || len(suffix) == 1 { |
| 390 | return sec, nil |
| 391 | } |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 392 | // Otherwise, we require a per-installation and per-instance Permissions file. |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 393 | if len(suffix) == 2 { |
| 394 | p, err := installationDirCore(suffix, config.Root) |
| 395 | if err != nil { |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 396 | return nil, verror.New(ErrOperationFailed, nil, fmt.Sprintf("newAppSpecificAuthorizer failed: %v", err)) |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 397 | } |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 398 | return pathperms.NewHierarchicalAuthorizer(PermsDir(config), path.Join(p, "acls"), getter) |
Asim Shankar | 6888519 | 2014-11-26 12:48:35 -0800 | [diff] [blame] | 399 | } |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 400 | // Use the special debugacls for instance/logs, instance/pprof, instance/stats. |
Robert Kroeger | 16ee22b | 2015-03-12 14:57:09 -0700 | [diff] [blame] | 401 | if len(suffix) > 3 && (suffix[3] == "logs" || suffix[3] == "pprof" || suffix[3] == "stats") { |
| 402 | p, err := instanceDir(config.Root, suffix[0:3]) |
| 403 | if err != nil { |
| 404 | return nil, verror.New(ErrOperationFailed, nil, fmt.Sprintf("newAppSpecificAuthorizer failed: %v", err)) |
| 405 | } |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 406 | return pathperms.NewHierarchicalAuthorizer(PermsDir(config), path.Join(p, "debugacls"), getter) |
Robert Kroeger | 16ee22b | 2015-03-12 14:57:09 -0700 | [diff] [blame] | 407 | } |
| 408 | |
Robert Kroeger | 7a8b222 | 2015-03-06 07:58:57 -0800 | [diff] [blame] | 409 | p, err := instanceDir(config.Root, suffix[0:3]) |
| 410 | if err != nil { |
| 411 | return nil, verror.New(ErrOperationFailed, nil, fmt.Sprintf("newAppSpecificAuthorizer failed: %v", err)) |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 412 | } |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 413 | return pathperms.NewHierarchicalAuthorizer(PermsDir(config), path.Join(p, "acls"), getter) |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 414 | } |