github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/mdserver_remote.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libkbfs 6 7 import ( 8 "fmt" 9 "sync" 10 "time" 11 12 "github.com/keybase/backoff" 13 "github.com/keybase/client/go/kbfs/idutil" 14 "github.com/keybase/client/go/kbfs/kbfscrypto" 15 "github.com/keybase/client/go/kbfs/kbfsmd" 16 "github.com/keybase/client/go/kbfs/libkey" 17 "github.com/keybase/client/go/kbfs/tlf" 18 "github.com/keybase/client/go/libkb" 19 "github.com/keybase/client/go/logger" 20 "github.com/keybase/client/go/protocol/keybase1" 21 "github.com/keybase/go-framed-msgpack-rpc/rpc" 22 "github.com/pkg/errors" 23 "golang.org/x/net/context" 24 ) 25 26 const ( 27 // MdServerBackgroundRekeyPeriod is how long the rekey checker 28 // waits between runs on average. The timer gets reset after 29 // every incoming FolderNeedsRekey RPC. 30 // The amount of wait is calculated in nextRekeyTime. 31 MdServerBackgroundRekeyPeriod = 1 * time.Hour 32 // MdServerDefaultPingIntervalSeconds is the default interval on which the 33 // client should contact the MD Server 34 MdServerDefaultPingIntervalSeconds = 10 35 // MdServerPingTimeout is how long to wait for a ping response 36 // before breaking the connection and trying to reconnect. 37 MdServerPingTimeout = 30 * time.Second 38 // mdServerLatestHandleTimeout is the timeout for checking the 39 // server for the latest handle before we use the cached value 40 // instead. 41 mdServerLatestHandleTimeout = 500 * time.Millisecond 42 // mdServerTimeoutWhenMDCached defines how long we wait for the 43 // latest MD to return from the server, when we've already read it 44 // from the disk cache. 45 mdServerTimeoutWhenMDCached = 500 * time.Millisecond 46 ) 47 48 // MDServerRemote is an implementation of the MDServer interface. 49 type MDServerRemote struct { 50 kbCtx Context 51 config Config 52 log traceLogger 53 deferLog traceLogger 54 mdSrvRemote rpc.Remote 55 connOpts rpc.ConnectionOpts 56 rpcLogFactory rpc.LogFactory 57 authToken *kbfscrypto.AuthToken 58 squelchRekey bool 59 pinger pinger 60 61 authenticatedMtx sync.RWMutex 62 isAuthenticated bool 63 64 connMu sync.RWMutex 65 conn *rpc.Connection 66 client keybase1.MetadataClient 67 68 observerMu sync.Mutex // protects observers 69 // chan is nil if we have unregistered locally, but not yet with 70 // the server. 71 observers map[tlf.ID]chan<- error 72 73 rekeyCancel context.CancelFunc 74 rekeyTimer *time.Timer 75 76 serverOffsetMu sync.RWMutex 77 serverOffsetKnown bool 78 serverOffset time.Duration 79 } 80 81 // Test that MDServerRemote fully implements the MDServer interface. 82 var _ MDServer = (*MDServerRemote)(nil) 83 84 // Test that MDServerRemote fully implements the KeyServer interface. 85 var _ libkey.KeyServer = (*MDServerRemote)(nil) 86 87 // Test that MDServerRemote fully implements the AuthTokenRefreshHandler interface. 88 var _ kbfscrypto.AuthTokenRefreshHandler = (*MDServerRemote)(nil) 89 90 // Test that MDServerRemote fully implements the ConnectionHandler interface. 91 var _ rpc.ConnectionHandler = (*MDServerRemote)(nil) 92 93 // NewMDServerRemote returns a new instance of MDServerRemote. 94 func NewMDServerRemote(kbCtx Context, config Config, srvRemote rpc.Remote, 95 rpcLogFactory rpc.LogFactory) *MDServerRemote { 96 log := config.MakeLogger("") 97 deferLog := log.CloneWithAddedDepth(1) 98 mdServer := &MDServerRemote{ 99 kbCtx: kbCtx, 100 config: config, 101 observers: make(map[tlf.ID]chan<- error), 102 log: traceLogger{log}, 103 deferLog: traceLogger{deferLog}, 104 mdSrvRemote: srvRemote, 105 rpcLogFactory: rpcLogFactory, 106 rekeyTimer: time.NewTimer(nextRekeyTime()), 107 } 108 109 mdServer.pinger = pinger{ 110 name: "MDServerRemote", 111 doPing: mdServer.pingOnce, 112 timeout: MdServerPingTimeout, 113 log: mdServer.log, 114 } 115 116 mdServer.authToken = kbfscrypto.NewAuthToken(config.Crypto(), 117 kbfsmd.ServerTokenServer, kbfsmd.ServerTokenExpireIn, 118 "libkbfs_mdserver_remote", VersionString(), mdServer) 119 constBackoff := backoff.NewConstantBackOff(RPCReconnectInterval) 120 firstConnectDelay := time.Duration(0) 121 if config.Mode().DelayInitialConnect() { 122 firstConnectDelay = libkb.RandomJitter(mdserverFirstConnectDelay) 123 } 124 mdServer.connOpts = rpc.ConnectionOpts{ 125 WrapErrorFunc: libkb.WrapError, 126 TagsFunc: libkb.LogTagsFromContext, 127 ReconnectBackoff: func() backoff.BackOff { return constBackoff }, 128 DialerTimeout: dialerTimeout, 129 FirstConnectDelayDuration: firstConnectDelay, 130 InitialReconnectBackoffWindow: func() time.Duration { return mdserverReconnectBackoffWindow }, 131 } 132 mdServer.initNewConnection() 133 134 // Check for rekey opportunities periodically. 135 rekeyCtx, rekeyCancel := context.WithCancel(context.Background()) 136 mdServer.rekeyCancel = rekeyCancel 137 if config.Mode().RekeyWorkers() > 0 { 138 go mdServer.backgroundRekeyChecker(rekeyCtx) 139 } 140 141 return mdServer 142 } 143 144 func (md *MDServerRemote) getIsAuthenticated() bool { 145 md.authenticatedMtx.RLock() 146 defer md.authenticatedMtx.RUnlock() 147 return md.isAuthenticated 148 } 149 150 func (md *MDServerRemote) setIsAuthenticated(isAuthenticated bool) { 151 md.authenticatedMtx.Lock() 152 defer md.authenticatedMtx.Unlock() 153 md.isAuthenticated = isAuthenticated 154 } 155 156 func (md *MDServerRemote) initNewConnection() { 157 md.connMu.Lock() 158 defer md.connMu.Unlock() 159 160 if md.conn != nil { 161 md.conn.Shutdown() 162 } 163 164 md.conn = rpc.NewTLSConnectionWithDialable(md.mdSrvRemote, kbfscrypto.GetRootCerts( 165 md.mdSrvRemote.Peek(), libkb.GetBundledCAsFromHost), 166 kbfsmd.ServerErrorUnwrapper{}, md, md.rpcLogFactory, 167 md.kbCtx.NewNetworkInstrumenter(keybase1.NetworkSource_REMOTE), 168 logger.LogOutputWithDepthAdder{Logger: md.config.MakeLogger("")}, 169 rpc.DefaultMaxFrameLength, md.connOpts, 170 libkb.NewProxyDialable(md.kbCtx.GetEnv())) 171 md.client = keybase1.MetadataClient{Cli: md.conn.GetClient()} 172 } 173 174 const reconnectTimeout = 10 * time.Second 175 176 func (md *MDServerRemote) reconnectContext(ctx context.Context) error { 177 md.connMu.Lock() 178 defer md.connMu.Unlock() 179 180 if md.conn != nil { 181 _, hasDeadline := ctx.Deadline() 182 if !hasDeadline { 183 var cancel context.CancelFunc 184 ctx, cancel = context.WithTimeout(ctx, reconnectTimeout) 185 defer cancel() 186 } 187 188 return md.conn.ForceReconnect(ctx) 189 } 190 191 md.initNewConnection() 192 return nil 193 } 194 195 func (md *MDServerRemote) reconnect() error { 196 return md.reconnectContext(context.Background()) 197 } 198 199 // RemoteAddress returns the remote mdserver this client is talking to 200 func (md *MDServerRemote) RemoteAddress() string { 201 return md.mdSrvRemote.String() 202 } 203 204 // HandlerName implements the ConnectionHandler interface. 205 func (*MDServerRemote) HandlerName() string { 206 return "MDServerRemote" 207 } 208 209 // OnConnect implements the ConnectionHandler interface. 210 func (md *MDServerRemote) OnConnect(ctx context.Context, 211 conn *rpc.Connection, client rpc.GenericClient, 212 server *rpc.Server) (err error) { 213 214 defer func() { 215 md.config.Reporter().OnlineStatusChanged(ctx, err == nil) 216 if err == nil { 217 md.config.Reporter().Notify(ctx, 218 connectionNotification(connectionStatusConnected)) 219 } 220 }() 221 222 md.log.CInfof(ctx, "OnConnect called with a new connection") 223 224 // we'll get replies asynchronously as to not block the connection 225 // for doing other active work for the user. they will be sent to 226 // the FolderNeedsRekey handler. 227 if err := server.Register(keybase1.MetadataUpdateProtocol(md)); err != nil { 228 if _, ok := err.(rpc.AlreadyRegisteredError); !ok { 229 return err 230 } 231 } 232 233 // reset auth -- using md.client here would cause problematic recursion. 234 c := keybase1.MetadataClient{Cli: client} 235 pingIntervalSeconds, err := md.resetAuth(ctx, c) 236 switch err.(type) { 237 case nil: 238 case idutil.NoCurrentSessionError: 239 md.log.CInfof(ctx, "Logged-out user") 240 default: 241 return err 242 } 243 244 md.config.KBFSOps().PushConnectionStatusChange(MDServiceName, nil) 245 246 // start pinging 247 md.pinger.resetTicker(pingIntervalSeconds) 248 return nil 249 } 250 251 type ctxMDServerResetKeyType int 252 253 const ( 254 // ctxMDServerResetKey identifies whether the current context has 255 // already passed through `MDServerRemote.resetAuth`. 256 ctxMDServerResetKey ctxMDServerResetKeyType = iota 257 ) 258 259 // resetAuth is called to reset the authorization on an MDServer 260 // connection. If this function returns 261 // idutil.NoCurrentSessionError, the caller should treat this as a 262 // logged-out user. 263 func (md *MDServerRemote) resetAuth( 264 ctx context.Context, c keybase1.MetadataClient) (int, error) { 265 ctx = context.WithValue(ctx, ctxMDServerResetKey, "1") 266 267 isAuthenticated := false 268 defer func() { 269 md.setIsAuthenticated(isAuthenticated) 270 }() 271 272 session, err := md.config.KBPKI().GetCurrentSession(ctx) 273 if err != nil { 274 md.log.CInfof(ctx, 275 "Error getting current session (%+v), skipping resetAuth", err) 276 return MdServerDefaultPingIntervalSeconds, err 277 } 278 279 challenge, err := c.GetChallenge(ctx) 280 if err != nil { 281 md.log.CWarningf(ctx, "challenge request error: %v", err) 282 return 0, err 283 } 284 md.log.CDebugf(ctx, "received challenge") 285 286 // get a new signature 287 signature, err := md.authToken.Sign(ctx, session.Name, session.UID, 288 session.VerifyingKey, challenge) 289 if err != nil { 290 md.log.CWarningf(ctx, "error signing authentication token: %v", err) 291 return 0, err 292 } 293 md.log.CDebugf(ctx, "authentication token signed") 294 295 // authenticate 296 pingIntervalSeconds, err := c.Authenticate(ctx, signature) 297 if err != nil { 298 md.log.CWarningf(ctx, "authentication error: %v", err) 299 return 0, err 300 } 301 md.log.CInfof(ctx, "authentication successful; ping interval: %ds", 302 pingIntervalSeconds) 303 304 isAuthenticated = true 305 306 md.authenticatedMtx.Lock() 307 if !md.isAuthenticated && md.config.Mode().RekeyWorkers() > 0 { 308 defer func() { 309 // request a list of folders needing rekey action 310 if err := md.getFoldersForRekey(ctx, c); err != nil { 311 md.log.CWarningf(ctx, "getFoldersForRekey failed with %v", err) 312 } 313 md.deferLog.CDebugf(ctx, 314 "requested list of folders for rekey") 315 }() 316 } 317 // Need to ensure that any conflicting thread gets the updated value 318 md.isAuthenticated = true 319 md.authenticatedMtx.Unlock() 320 321 return pingIntervalSeconds, nil 322 } 323 324 func (md *MDServerRemote) getClient() keybase1.MetadataClient { 325 md.connMu.RLock() 326 defer md.connMu.RUnlock() 327 return md.client 328 } 329 330 // RefreshAuthToken implements the AuthTokenRefreshHandler interface. 331 func (md *MDServerRemote) RefreshAuthToken(ctx context.Context) { 332 md.log.CDebugf(ctx, "MDServerRemote: Refreshing auth token...") 333 334 if v := ctx.Value(ctxMDServerResetKey); v != nil { 335 md.log.CDebugf(ctx, "Avoiding resetAuth recursion") 336 return 337 } 338 339 _, err := md.resetAuth(ctx, md.getClient()) 340 switch err.(type) { 341 case nil: 342 md.log.CInfof(ctx, "MDServerRemote: auth token refreshed") 343 case idutil.NoCurrentSessionError: 344 md.log.CInfof(ctx, 345 "MDServerRemote: no session available, connection remains anonymous") 346 default: 347 md.log.CInfof(ctx, 348 "MDServerRemote: error refreshing auth token: %v", err) 349 err = md.reconnect() 350 if err != nil { 351 md.log.CWarningf(ctx, 352 "MDServerRemote: error calling md.reconnect(): %v", err) 353 } 354 } 355 } 356 357 func (md *MDServerRemote) pingOnce(ctx context.Context) { 358 clock := md.config.Clock() 359 beforePing := clock.Now() 360 resp, err := md.getClient().Ping2(ctx) 361 if err == context.DeadlineExceeded { 362 if md.getIsAuthenticated() { 363 md.log.CInfof(ctx, "Ping timeout -- reinitializing connection") 364 if err = md.reconnect(); err != nil { 365 md.log.CInfof(ctx, "reconnect error: %v", err) 366 } 367 } else { 368 md.log.CInfof(ctx, "Ping timeout but not reinitializing") 369 } 370 return 371 } else if err != nil { 372 md.log.CInfof(ctx, "MDServerRemote: ping error %s", err) 373 return 374 } 375 afterPing := clock.Now() 376 pingLatency := afterPing.Sub(beforePing) 377 if md.serverOffset > 0 && pingLatency > 5*time.Second { 378 md.log.CDebugf(ctx, "Ignoring large ping time: %s", 379 pingLatency) 380 return 381 } 382 383 serverTimeNow := 384 keybase1.FromTime(resp.Timestamp).Add(pingLatency / 2) 385 func() { 386 md.serverOffsetMu.Lock() 387 defer md.serverOffsetMu.Unlock() 388 // Estimate the server offset, assuming a balanced 389 // round trip latency (and 0 server processing 390 // latency). Calculate it so that it can be added 391 // to a server timestamp in order to get the local 392 // time of a server-timestamped event. 393 md.serverOffset = afterPing.Sub(serverTimeNow) 394 md.serverOffsetKnown = true 395 }() 396 } 397 398 // OnConnectError implements the ConnectionHandler interface. 399 func (md *MDServerRemote) OnConnectError(err error, wait time.Duration) { 400 md.log.CWarningf(context.TODO(), 401 "MDServerRemote: connection error: %q; retrying in %s", err, wait) 402 // TODO: it might make sense to show something to the user if this is 403 // due to authentication, for example. 404 md.cancelObservers() 405 md.pinger.cancelTicker() 406 if md.authToken != nil { 407 md.authToken.Shutdown() 408 } 409 410 md.config.KBFSOps().PushConnectionStatusChange(MDServiceName, err) 411 md.config.Reporter().OnlineStatusChanged(context.Background(), false) 412 } 413 414 // OnDoCommandError implements the ConnectionHandler interface. 415 func (md *MDServerRemote) OnDoCommandError(err error, wait time.Duration) { 416 md.log.CWarningf(context.TODO(), 417 "MDServerRemote: DoCommand error: %q; retrying in %s", err, wait) 418 // Only push errors that should not be retried as connection status changes. 419 if !md.ShouldRetry("", err) { 420 md.config.KBFSOps().PushConnectionStatusChange(MDServiceName, err) 421 md.config.Reporter().OnlineStatusChanged(context.Background(), false) 422 } 423 } 424 425 // OnDisconnected implements the ConnectionHandler interface. 426 func (md *MDServerRemote) OnDisconnected(ctx context.Context, 427 status rpc.DisconnectStatus) { 428 if status == rpc.StartingNonFirstConnection { 429 md.log.CWarningf(ctx, "MDServerRemote is disconnected") 430 md.config.Reporter().Notify(ctx, 431 connectionNotification(connectionStatusDisconnected)) 432 md.config.Reporter().OnlineStatusChanged(ctx, false) 433 } 434 435 func() { 436 md.serverOffsetMu.Lock() 437 defer md.serverOffsetMu.Unlock() 438 md.serverOffsetKnown = false 439 md.serverOffset = 0 440 }() 441 442 md.setIsAuthenticated(false) 443 444 md.cancelObservers() 445 md.pinger.cancelTicker() 446 if md.authToken != nil { 447 md.authToken.Shutdown() 448 } 449 md.config.RekeyQueue().Shutdown() 450 md.config.SetRekeyQueue(NewRekeyQueueStandard(md.config)) 451 // Reset the timer since we will get folders for rekey again on 452 // the re-connect. 453 md.resetRekeyTimer() 454 455 if status == rpc.StartingNonFirstConnection { 456 md.config.KBFSOps().PushConnectionStatusChange(MDServiceName, errDisconnected{}) 457 md.config.Reporter().OnlineStatusChanged(ctx, false) 458 } 459 } 460 461 // ShouldRetry implements the ConnectionHandler interface. 462 func (md *MDServerRemote) ShouldRetry(name string, err error) bool { 463 _, shouldThrottle := err.(kbfsmd.ServerErrorThrottle) 464 return shouldThrottle 465 } 466 467 // ShouldRetryOnConnect implements the ConnectionHandler interface. 468 func (md *MDServerRemote) ShouldRetryOnConnect(err error) bool { 469 _, inputCanceled := err.(libkb.InputCanceledError) 470 return !inputCanceled 471 } 472 473 // CheckReachability implements the MDServer interface. 474 func (md *MDServerRemote) CheckReachability(ctx context.Context) { 475 conn, err := libkb.ProxyDialTimeout(md.kbCtx.GetEnv(), "tcp", 476 // The peeked address is the top choice in most cases. 477 md.mdSrvRemote.Peek(), MdServerPingTimeout) 478 if err != nil { 479 if md.getIsAuthenticated() { 480 md.log.CInfof(ctx, "MDServerRemote: CheckReachability(): "+ 481 "failed to connect, reconnecting: %s", err.Error()) 482 if err = md.reconnectContext(ctx); err != nil { 483 md.log.CInfof(ctx, "reconnect error: %v", err) 484 } 485 } else { 486 md.log.CInfof(ctx, "MDServerRemote: CheckReachability(): "+ 487 "failed to connect (%s), but not reconnecting", err.Error()) 488 } 489 } 490 if conn != nil { 491 conn.Close() 492 } 493 } 494 495 // Signal errors and clear any registered observers. 496 func (md *MDServerRemote) cancelObservers() { 497 md.observerMu.Lock() 498 defer md.observerMu.Unlock() 499 // fire errors for any registered observers 500 for id, observerChan := range md.observers { 501 md.signalObserverLocked(observerChan, id, MDServerDisconnected{}) 502 } 503 } 504 505 // CancelRegistration implements the MDServer interface for MDServerRemote. 506 func (md *MDServerRemote) CancelRegistration(ctx context.Context, id tlf.ID) { 507 md.observerMu.Lock() 508 defer md.observerMu.Unlock() 509 observerChan, ok := md.observers[id] 510 if !ok { 511 // not registered 512 return 513 } 514 515 // signal that we've seen the update 516 md.signalObserverLocked( 517 observerChan, id, errors.New("Registration canceled")) 518 // Setting nil here indicates that the remote MD server thinks 519 // we're still registered, though locally no one is listening. 520 md.observers[id] = nil 521 } 522 523 // Signal an observer. The observer lock must be held. 524 func (md *MDServerRemote) signalObserverLocked(observerChan chan<- error, id tlf.ID, err error) { 525 if observerChan != nil { 526 observerChan <- err 527 close(observerChan) 528 } 529 delete(md.observers, id) 530 } 531 532 func (md *MDServerRemote) getLatestFromCache( 533 ctx context.Context, tlfID tlf.ID) (*RootMetadataSigned, error) { 534 if md.config.DiskMDCache() == nil { 535 return nil, nil 536 } 537 538 buf, ver, timestamp, err := md.config.DiskMDCache().Get(ctx, tlfID) 539 if err != nil { 540 return nil, err 541 } 542 return DecodeRootMetadataSigned( 543 md.config.Codec(), tlfID, ver, md.config.MetadataVersion(), buf, 544 timestamp) 545 } 546 547 // Helper used to retrieve metadata blocks from the MD server. 548 func (md *MDServerRemote) get(ctx context.Context, arg keybase1.GetMetadataArg) ( 549 tlfID tlf.ID, rmdses []*RootMetadataSigned, err error) { 550 // request 551 response, err := md.getClient().GetMetadata(ctx, arg) 552 if err != nil { 553 return tlf.ID{}, nil, err 554 } 555 556 // response 557 tlfID, err = tlf.ParseID(response.FolderID) 558 if err != nil { 559 return tlf.ID{}, nil, err 560 } 561 562 // deserialize blocks 563 rmdses = make([]*RootMetadataSigned, len(response.MdBlocks)) 564 diskMDCache := md.config.DiskMDCache() 565 for i, block := range response.MdBlocks { 566 ver, max := kbfsmd.MetadataVer(block.Version), md.config.MetadataVersion() 567 timestamp := keybase1.FromTime(block.Timestamp) 568 rmds, err := DecodeRootMetadataSigned( 569 md.config.Codec(), tlfID, ver, max, block.Block, 570 keybase1.FromTime(block.Timestamp)) 571 if err != nil { 572 return tlf.ID{}, nil, err 573 } 574 rmdses[i] = rmds 575 if diskMDCache != nil && rmds.MD.MergedStatus() == kbfsmd.Merged { 576 err = diskMDCache.Stage( 577 ctx, tlfID, rmds.MD.RevisionNumber(), block.Block, ver, 578 timestamp) 579 if err != nil { 580 return tlf.ID{}, nil, err 581 } 582 } 583 } 584 return tlfID, rmdses, nil 585 } 586 587 // GetForHandle implements the MDServer interface for MDServerRemote. 588 func (md *MDServerRemote) GetForHandle(ctx context.Context, 589 handle tlf.Handle, mStatus kbfsmd.MergeStatus, lockBeforeGet *keybase1.LockID) ( 590 tlfID tlf.ID, rmds *RootMetadataSigned, err error) { 591 ctx = rpc.WithFireNow(ctx) 592 // TODO: Ideally, *tlf.Handle would have a nicer String() function. 593 md.log.LazyTrace(ctx, "MDServer: GetForHandle %+v %s", handle, mStatus) 594 defer func() { 595 md.deferLog.LazyTrace(ctx, "MDServer: GetForHandle %+v %s done (err=%v)", handle, mStatus, err) 596 }() 597 598 encodedHandle, err := md.config.Codec().Encode(handle) 599 if err != nil { 600 return tlf.ID{}, nil, err 601 } 602 // kbfsmd.BranchID needs to be present when Unmerged is true; 603 // kbfsmd.NullBranchID signals that the folder's current branch ID 604 // should be looked up. 605 arg := keybase1.GetMetadataArg{ 606 FolderHandle: encodedHandle, 607 BranchID: kbfsmd.NullBranchID.String(), 608 Unmerged: mStatus == kbfsmd.Unmerged, 609 LockBeforeGet: lockBeforeGet, 610 } 611 612 id, rmdses, err := md.get(ctx, arg) 613 if err != nil { 614 return tlf.ID{}, nil, err 615 } 616 if len(rmdses) == 0 { 617 return id, nil, nil 618 } 619 // TODO: Error if server returns more than one rmds. 620 return id, rmdses[0], nil 621 } 622 623 // GetForTLF implements the MDServer interface for MDServerRemote. 624 func (md *MDServerRemote) GetForTLF(ctx context.Context, id tlf.ID, 625 bid kbfsmd.BranchID, mStatus kbfsmd.MergeStatus, lockBeforeGet *keybase1.LockID) (rmds *RootMetadataSigned, err error) { 626 ctx = rpc.WithFireNow(ctx) 627 md.log.LazyTrace(ctx, "MDServer: GetForTLF %s %s %s", id, bid, mStatus) 628 defer func() { 629 md.deferLog.LazyTrace(ctx, "MDServer: GetForTLF %s %s %s done (err=%v)", id, bid, mStatus, err) 630 }() 631 632 var cachedRmds *RootMetadataSigned 633 getCtx := ctx 634 if mStatus == kbfsmd.Merged && lockBeforeGet == nil { 635 cachedRmds, err = md.getLatestFromCache(ctx, id) 636 if err == nil && cachedRmds != nil { 637 md.log.CDebugf(ctx, 638 "Read revision %d for TLF %s from the disk cache", 639 cachedRmds.MD.RevisionNumber(), id) 640 var cancel context.CancelFunc 641 getCtx, cancel = context.WithTimeout( 642 ctx, mdServerTimeoutWhenMDCached) 643 defer cancel() 644 } 645 } 646 647 arg := keybase1.GetMetadataArg{ 648 FolderID: id.String(), 649 BranchID: bid.String(), 650 Unmerged: mStatus == kbfsmd.Unmerged, 651 LockBeforeGet: lockBeforeGet, 652 } 653 654 _, rmdses, err := md.get(getCtx, arg) 655 switch errors.Cause(err) { 656 case nil: 657 case context.DeadlineExceeded: 658 if cachedRmds != nil { 659 md.log.CDebugf(ctx, "Can't contact server; using cached MD") 660 return cachedRmds, nil 661 } 662 return nil, err 663 default: 664 return nil, err 665 } 666 667 if len(rmdses) == 0 { 668 return nil, nil 669 } 670 671 if cachedRmds != nil { 672 md.log.CDebugf(ctx, "Read revision %d for TLF %s from the server", 673 rmdses[0].MD.RevisionNumber(), id) 674 } 675 676 // TODO: Error if server returns more than one rmds. 677 return rmdses[0], nil 678 } 679 680 // GetForTLFByTime implements the MDServer interface for MDServerRemote. 681 func (md *MDServerRemote) GetForTLFByTime( 682 ctx context.Context, id tlf.ID, serverTime time.Time) ( 683 rmds *RootMetadataSigned, err error) { 684 ctx = rpc.WithFireNow(ctx) 685 md.log.LazyTrace(ctx, "MDServer: GetForTLFByTime %s %s", id, serverTime) 686 defer func() { 687 md.deferLog.LazyTrace( 688 ctx, "MDServer: GetForTLFByTime %s %s done (err=%v)", 689 id, serverTime, err) 690 }() 691 692 arg := keybase1.GetMetadataByTimestampArg{ 693 FolderID: id.String(), 694 ServerTime: keybase1.ToTime(serverTime), 695 } 696 697 block, err := md.getClient().GetMetadataByTimestamp(ctx, arg) 698 if err != nil { 699 return nil, err 700 } else if len(block.Block) == 0 { 701 return nil, errors.Errorf("No revision available at %s", serverTime) 702 } 703 704 ver, max := kbfsmd.MetadataVer(block.Version), md.config.MetadataVersion() 705 rmds, err = DecodeRootMetadataSigned( 706 md.config.Codec(), id, ver, max, block.Block, 707 keybase1.FromTime(block.Timestamp)) 708 if err != nil { 709 return nil, err 710 } 711 return rmds, nil 712 } 713 714 // GetRange implements the MDServer interface for MDServerRemote. 715 func (md *MDServerRemote) GetRange(ctx context.Context, id tlf.ID, 716 bid kbfsmd.BranchID, mStatus kbfsmd.MergeStatus, 717 start, stop kbfsmd.Revision, lockBeforeGet *keybase1.LockID) ( 718 rmdses []*RootMetadataSigned, err error) { 719 ctx = rpc.WithFireNow(ctx) 720 md.log.LazyTrace(ctx, "MDServer: GetRange %s %s %s %d-%d", id, bid, mStatus, start, stop) 721 defer func() { 722 md.deferLog.LazyTrace(ctx, "MDServer: GetRange %s %s %s %d-%d done (err=%v)", id, bid, mStatus, start, stop, err) 723 }() 724 725 arg := keybase1.GetMetadataArg{ 726 FolderID: id.String(), 727 BranchID: bid.String(), 728 Unmerged: mStatus == kbfsmd.Unmerged, 729 StartRevision: start.Number(), 730 StopRevision: stop.Number(), 731 LockBeforeGet: lockBeforeGet, 732 } 733 734 _, rmds, err := md.get(ctx, arg) 735 return rmds, err 736 } 737 738 // Put implements the MDServer interface for MDServerRemote. 739 func (md *MDServerRemote) Put(ctx context.Context, rmds *RootMetadataSigned, 740 extra kbfsmd.ExtraMetadata, lockContext *keybase1.LockContext, 741 priority keybase1.MDPriority) (err error) { 742 ctx = rpc.WithFireNow(ctx) 743 md.log.LazyTrace(ctx, "MDServer: Put %s %d", rmds.MD.TlfID(), rmds.MD.RevisionNumber()) 744 defer func() { 745 md.deferLog.LazyTrace(ctx, "MDServer: Put %s %d done (err=%v)", rmds.MD.TlfID(), rmds.MD.RevisionNumber(), err) 746 }() 747 748 // encode MD block 749 rmdsBytes, err := kbfsmd.EncodeRootMetadataSigned(md.config.Codec(), &rmds.RootMetadataSigned) 750 if err != nil { 751 return err 752 } 753 754 // put request 755 arg := keybase1.PutMetadataArg{ 756 MdBlock: keybase1.MDBlock{ 757 Version: int(rmds.Version()), 758 Block: rmdsBytes, 759 }, 760 LogTags: nil, 761 Priority: priority, 762 } 763 if lockContext != nil { 764 copied := *lockContext 765 arg.LockContext = &copied 766 } 767 768 if rmds.Version() != kbfsmd.SegregatedKeyBundlesVer { 769 if extra != nil { 770 return fmt.Errorf("Unexpected non-nil extra: %+v", extra) 771 } 772 } else if extra != nil { 773 // For now, if we have a non-nil extra, it must be 774 // *ExtraMetadataV3, but in the future it might be 775 // some other type (e.g., *ExtraMetadataV4). 776 extraV3, ok := extra.(*kbfsmd.ExtraMetadataV3) 777 if !ok { 778 return fmt.Errorf("Extra of unexpected type %T", extra) 779 } 780 781 // Add any new key bundles. 782 if extraV3.IsWriterKeyBundleNew() { 783 wkbBytes, err := md.config.Codec().Encode(extraV3.GetWriterKeyBundle()) 784 if err != nil { 785 return err 786 } 787 arg.WriterKeyBundle = keybase1.KeyBundle{ 788 Version: int(rmds.Version()), 789 Bundle: wkbBytes, 790 } 791 } 792 if extraV3.IsReaderKeyBundleNew() { 793 rkbBytes, err := md.config.Codec().Encode(extraV3.GetReaderKeyBundle()) 794 if err != nil { 795 return err 796 } 797 arg.ReaderKeyBundle = keybase1.KeyBundle{ 798 Version: int(rmds.Version()), 799 Bundle: rkbBytes, 800 } 801 } 802 } 803 804 err = md.getClient().PutMetadata(ctx, arg) 805 if err != nil { 806 return err 807 } 808 809 // Stage the new MD if needed. 810 diskMDCache := md.config.DiskMDCache() 811 if diskMDCache != nil && rmds.MD.MergedStatus() == kbfsmd.Merged { 812 // Guess the server timestamp by using the local offset. 813 // TODO: the server should return this, and/or we should fetch 814 // it explicitly. 815 revTime := md.config.Clock().Now() 816 if offset, ok := md.OffsetFromServerTime(); ok { 817 revTime = revTime.Add(-offset) 818 } 819 err = diskMDCache.Stage( 820 ctx, rmds.MD.TlfID(), rmds.MD.RevisionNumber(), rmdsBytes, 821 rmds.Version(), revTime) 822 if err != nil { 823 return err 824 } 825 } 826 return nil 827 } 828 829 // Lock implements the MDServer interface for MDServerRemote. 830 func (md *MDServerRemote) Lock(ctx context.Context, 831 tlfID tlf.ID, lockID keybase1.LockID) error { 832 ctx = rpc.WithFireNow(ctx) 833 md.log.LazyTrace(ctx, "MDServer: Lock %s %s", tlfID, lockID) 834 defer func() { 835 md.deferLog.LazyTrace(ctx, "MDServer: Lock %s %s", tlfID, lockID) 836 }() 837 return md.getClient().Lock(ctx, keybase1.LockArg{ 838 FolderID: tlfID.String(), 839 LockID: lockID, 840 }) 841 } 842 843 // ReleaseLock implements the MDServer interface for MDServerRemote. 844 func (md *MDServerRemote) ReleaseLock(ctx context.Context, 845 tlfID tlf.ID, lockID keybase1.LockID) error { 846 ctx = rpc.WithFireNow(ctx) 847 md.log.LazyTrace(ctx, "MDServer: ReleaseLock %s %s", tlfID, lockID) 848 defer func() { 849 md.deferLog.LazyTrace(ctx, "MDServer: ReleaseLock %s %s", tlfID, lockID) 850 }() 851 return md.getClient().ReleaseLock(ctx, keybase1.ReleaseLockArg{ 852 FolderID: tlfID.String(), 853 LockID: lockID, 854 }) 855 } 856 857 // StartImplicitTeamMigration implements the MDServer interface. 858 func (md *MDServerRemote) StartImplicitTeamMigration( 859 ctx context.Context, id tlf.ID) (err error) { 860 ctx = rpc.WithFireNow(ctx) 861 md.log.LazyTrace(ctx, 862 "MDServer: StartImplicitTeamMigration %s", id) 863 defer func() { 864 md.deferLog.LazyTrace(ctx, 865 "MDServer: StartImplicitTeamMigration %s (err=%v)", id, err) 866 }() 867 868 return md.getClient().StartImplicitTeamMigration(ctx, id.String()) 869 } 870 871 // PruneBranch implements the MDServer interface for MDServerRemote. 872 func (md *MDServerRemote) PruneBranch( 873 ctx context.Context, id tlf.ID, bid kbfsmd.BranchID) (err error) { 874 ctx = rpc.WithFireNow(ctx) 875 md.log.LazyTrace(ctx, "MDServer: PruneBranch %s %s", id, bid) 876 defer func() { 877 md.deferLog.LazyTrace(ctx, "MDServer: PruneBranch %s %s (err=%v)", id, bid, err) 878 }() 879 880 arg := keybase1.PruneBranchArg{ 881 FolderID: id.String(), 882 BranchID: bid.String(), 883 LogTags: nil, 884 } 885 return md.getClient().PruneBranch(ctx, arg) 886 } 887 888 // MetadataUpdate implements the MetadataUpdateProtocol interface. 889 func (md *MDServerRemote) MetadataUpdate(_ context.Context, arg keybase1.MetadataUpdateArg) error { 890 id, err := tlf.ParseID(arg.FolderID) 891 if err != nil { 892 return err 893 } 894 895 md.observerMu.Lock() 896 defer md.observerMu.Unlock() 897 observerChan, ok := md.observers[id] 898 if !ok { 899 // not registered 900 return nil 901 } 902 903 // signal that we've seen the update 904 md.signalObserverLocked(observerChan, id, nil) 905 return nil 906 } 907 908 // FoldersNeedRekey implements the MetadataUpdateProtocol interface. 909 func (md *MDServerRemote) FoldersNeedRekey(ctx context.Context, 910 requests []keybase1.RekeyRequest) error { 911 if md.squelchRekey { 912 md.log.CDebugf(ctx, "MDServerRemote: rekey updates squelched for testing") 913 return nil 914 } 915 for _, req := range requests { 916 id, err := tlf.ParseID(req.FolderID) 917 if err != nil { 918 return err 919 } 920 md.log.CDebugf(ctx, "MDServerRemote: folder needs rekey: %s", id.String()) 921 // queue the folder for rekeying 922 md.config.RekeyQueue().Enqueue(id) 923 } 924 // Reset the timer in case there are a lot of rekey folders 925 // dribbling in from the server still. 926 md.resetRekeyTimer() 927 return nil 928 } 929 930 // FolderNeedsRekey implements the MetadataUpdateProtocol interface. 931 func (md *MDServerRemote) FolderNeedsRekey(ctx context.Context, 932 arg keybase1.FolderNeedsRekeyArg) error { 933 id, err := tlf.ParseID(arg.FolderID) 934 if err != nil { 935 return err 936 } 937 md.log.CDebugf(ctx, "MDServerRemote: folder needs rekey: %s", id.String()) 938 if md.squelchRekey { 939 md.log.CDebugf(ctx, "MDServerRemote: rekey updates squelched for testing") 940 return nil 941 } 942 // queue the folder for rekeying 943 md.config.RekeyQueue().Enqueue(id) 944 // Reset the timer in case there are a lot of rekey folders 945 // dribbling in from the server still. 946 md.resetRekeyTimer() 947 return nil 948 } 949 950 func (md *MDServerRemote) getConn() *rpc.Connection { 951 md.connMu.RLock() 952 defer md.connMu.RUnlock() 953 return md.conn 954 } 955 956 // RegisterForUpdate implements the MDServer interface for MDServerRemote. 957 func (md *MDServerRemote) RegisterForUpdate(ctx context.Context, id tlf.ID, 958 currHead kbfsmd.Revision) (<-chan error, error) { 959 arg := keybase1.RegisterForUpdatesArg{ 960 FolderID: id.String(), 961 CurrRevision: currHead.Number(), 962 LogTags: nil, 963 } 964 965 // register 966 var c chan error 967 conn := md.getConn() 968 err := conn.DoCommand(ctx, "register", 0, func(rawClient rpc.GenericClient) error { 969 // set up the server to receive updates, since we may 970 // get disconnected between retries. 971 server := conn.GetServer() 972 err := server.Register(keybase1.MetadataUpdateProtocol(md)) 973 if err != nil { 974 if _, ok := err.(rpc.AlreadyRegisteredError); !ok { 975 return err 976 } 977 } 978 // TODO: Do something with server.Err() when server is 979 // done? 980 server.Run() 981 982 // keep re-adding the observer on retries, since 983 // disconnects or connection errors clear observers. 984 alreadyRegistered := func() bool { 985 md.observerMu.Lock() 986 defer md.observerMu.Unlock() 987 // It's possible for a nil channel to be in 988 // `md.observers`, if we are still registered with the 989 // server after a previous cancellation. 990 existingCh, alreadyRegistered := md.observers[id] 991 if existingCh != nil { 992 panic(fmt.Sprintf( 993 "Attempted double-registration for folder: %s", id)) 994 } 995 c = make(chan error, 1) 996 md.observers[id] = c 997 return alreadyRegistered 998 }() 999 if alreadyRegistered { 1000 return nil 1001 } 1002 // Use this instead of md.client since we're already 1003 // inside a DoCommand(). 1004 c := keybase1.MetadataClient{Cli: rawClient} 1005 err = c.RegisterForUpdates(ctx, arg) 1006 if err != nil { 1007 func() { 1008 md.observerMu.Lock() 1009 defer md.observerMu.Unlock() 1010 // we could've been canceled by a shutdown so look this up 1011 // again before closing and deleting. 1012 if updateChan, ok := md.observers[id]; ok { 1013 close(updateChan) 1014 delete(md.observers, id) 1015 } 1016 }() 1017 } 1018 return err 1019 }) 1020 if err != nil { 1021 c = nil 1022 } 1023 1024 return c, err 1025 } 1026 1027 // TruncateLock implements the MDServer interface for MDServerRemote. 1028 func (md *MDServerRemote) TruncateLock(ctx context.Context, id tlf.ID) ( 1029 locked bool, err error) { 1030 ctx = rpc.WithFireNow(ctx) 1031 md.log.LazyTrace(ctx, "MDServer: TruncateLock %s", id) 1032 defer func() { 1033 md.deferLog.LazyTrace(ctx, "MDServer: TruncateLock %s (err=%v)", id, err) 1034 }() 1035 return md.getClient().TruncateLock(ctx, id.String()) 1036 } 1037 1038 // TruncateUnlock implements the MDServer interface for MDServerRemote. 1039 func (md *MDServerRemote) TruncateUnlock(ctx context.Context, id tlf.ID) ( 1040 unlocked bool, err error) { 1041 ctx = rpc.WithFireNow(ctx) 1042 md.log.LazyTrace(ctx, "MDServer: TruncateUnlock %s", id) 1043 defer func() { 1044 md.deferLog.LazyTrace(ctx, "MDServer: TruncateUnlock %s (err=%v)", id, err) 1045 }() 1046 return md.getClient().TruncateUnlock(ctx, id.String()) 1047 } 1048 1049 func (md *MDServerRemote) getLatestHandleFromCache( 1050 ctx context.Context, id tlf.ID) (handle tlf.Handle, err error) { 1051 rmds, err := md.getLatestFromCache(ctx, id) 1052 if err != nil { 1053 return tlf.Handle{}, err 1054 } 1055 if rmds == nil { 1056 return tlf.Handle{}, errors.Errorf("No cache MD for %s", id) 1057 } 1058 if rmds.MD.TypeForKeying() != tlf.TeamKeying { 1059 return tlf.Handle{}, errors.Errorf( 1060 "Cached MD for %s is not team-keyed", id) 1061 } 1062 1063 // We can only get the latest handle for team-keyed TLFs. 1064 return rmds.MD.MakeBareTlfHandle(nil) 1065 } 1066 1067 // GetLatestHandleForTLF implements the MDServer interface for MDServerRemote. 1068 func (md *MDServerRemote) GetLatestHandleForTLF(ctx context.Context, id tlf.ID) ( 1069 handle tlf.Handle, err error) { 1070 ctx = rpc.WithFireNow(ctx) 1071 md.log.LazyTrace(ctx, "MDServer: GetLatestHandle %s", id) 1072 defer func() { 1073 md.deferLog.LazyTrace(ctx, "MDServer: GetLatestHandle %s (err=%v)", id, err) 1074 }() 1075 1076 handle, handleErr := md.getLatestHandleFromCache(ctx, id) 1077 if handleErr == nil { 1078 if !md.IsConnected() { 1079 md.log.CDebugf(ctx, 1080 "Got latest handle for %s from cache when mdserver is "+ 1081 "disconnected", id) 1082 return handle, nil 1083 } 1084 md.log.CDebugf(ctx, 1085 "Setting a quick timeout when a cached handle is available "+ 1086 "for TLF %s", id) 1087 var cancel context.CancelFunc 1088 ctx, cancel = context.WithTimeout(ctx, mdServerLatestHandleTimeout) 1089 defer cancel() 1090 } 1091 1092 buf, err := md.getClient().GetLatestFolderHandle(ctx, id.String()) 1093 switch errors.Cause(err) { 1094 case nil: 1095 case context.DeadlineExceeded: 1096 if handleErr == nil { 1097 md.log.CDebugf(ctx, 1098 "Got latest handle for %s from cache when mdserver can't "+ 1099 "be reached quickly", id) 1100 return handle, nil 1101 } 1102 return tlf.Handle{}, err 1103 default: 1104 return tlf.Handle{}, err 1105 } 1106 if err := md.config.Codec().Decode(buf, &handle); err != nil { 1107 return tlf.Handle{}, err 1108 } 1109 return handle, nil 1110 } 1111 1112 // OffsetFromServerTime implements the MDServer interface for 1113 // MDServerRemote. 1114 func (md *MDServerRemote) OffsetFromServerTime() (time.Duration, bool) { 1115 md.serverOffsetMu.RLock() 1116 defer md.serverOffsetMu.RUnlock() 1117 return md.serverOffset, md.serverOffsetKnown 1118 } 1119 1120 // CheckForRekeys implements the MDServer interface. 1121 func (md *MDServerRemote) CheckForRekeys(ctx context.Context) <-chan error { 1122 // Wait 5 seconds before asking for rekeys, because the server 1123 // could have an out-of-date cache if we ask too soon. Why 5 1124 // seconds you ask? See `pollWait` in 1125 // github.com/keybase/client/go/auth/user_keys_api.go. We don't 1126 // use that value directly since there's no guarantee the server 1127 // is using the same value. TODO: the server should tell us what 1128 // value it is using. 1129 c := make(chan error, 1) 1130 if md.config.Mode().RekeyWorkers() == 0 { 1131 c <- nil 1132 return c 1133 } 1134 1135 // This is likely called in response to a service event from 1136 // keybase_service_base. So attach it with FireNow. 1137 ctx = rpc.WithFireNow(ctx) 1138 1139 time.AfterFunc(5*time.Second, func() { 1140 md.log.CInfof(ctx, "CheckForRekeys: checking for rekeys") 1141 select { 1142 case <-ctx.Done(): 1143 c <- ctx.Err() 1144 default: 1145 } 1146 if err := md.getFoldersForRekey(ctx, md.getClient()); err != nil { 1147 md.log.CDebugf(ctx, "getFoldersForRekey failed during "+ 1148 "CheckForRekeys: %v", err) 1149 c <- err 1150 } 1151 md.resetRekeyTimer() 1152 c <- nil 1153 }) 1154 return c 1155 } 1156 1157 // getFoldersForRekey registers to receive updates about folders needing rekey actions. 1158 func (md *MDServerRemote) getFoldersForRekey(ctx context.Context, 1159 client keybase1.MetadataClient) error { 1160 // get this device's crypt public key 1161 session, err := md.config.KBPKI().GetCurrentSession(ctx) 1162 if err != nil { 1163 return err 1164 } 1165 return client.GetFoldersForRekey(ctx, session.CryptPublicKey.KID()) 1166 } 1167 1168 // Shutdown implements the MDServer interface for MDServerRemote. 1169 func (md *MDServerRemote) Shutdown() { 1170 md.connMu.Lock() 1171 defer md.connMu.Unlock() 1172 1173 // close the connection 1174 md.conn.Shutdown() 1175 // cancel pending observers 1176 md.cancelObservers() 1177 // cancel the ping ticker 1178 md.pinger.cancelTicker() 1179 // cancel the auth token ticker 1180 if md.authToken != nil { 1181 md.authToken.Shutdown() 1182 } 1183 if md.rekeyCancel != nil { 1184 md.rekeyCancel() 1185 } 1186 } 1187 1188 // IsConnected implements the MDServer interface for MDServerLocal 1189 func (md *MDServerRemote) IsConnected() bool { 1190 conn := md.getConn() 1191 return conn != nil && conn.IsConnected() 1192 } 1193 1194 // 1195 // The below methods support the MD server acting as the key server. 1196 // This will be the case for v1 of KBFS but we may move to our own 1197 // separate key server at some point. 1198 // 1199 1200 // GetTLFCryptKeyServerHalf is an implementation of the KeyServer interface. 1201 func (md *MDServerRemote) GetTLFCryptKeyServerHalf(ctx context.Context, 1202 serverHalfID kbfscrypto.TLFCryptKeyServerHalfID, 1203 cryptKey kbfscrypto.CryptPublicKey) ( 1204 serverHalf kbfscrypto.TLFCryptKeyServerHalf, err error) { 1205 ctx = rpc.WithFireNow(ctx) 1206 md.log.LazyTrace(ctx, "KeyServer: GetTLFCryptKeyServerHalf %s", serverHalfID) 1207 defer func() { 1208 md.deferLog.LazyTrace(ctx, "KeyServer: GetTLFCryptKeyServerHalf %s (err=%v)", serverHalfID, err) 1209 }() 1210 1211 // encode the ID 1212 idBytes, err := md.config.Codec().Encode(serverHalfID) 1213 if err != nil { 1214 return 1215 } 1216 1217 // get the key 1218 arg := keybase1.GetKeyArg{ 1219 KeyHalfID: idBytes, 1220 DeviceKID: cryptKey.KID().String(), 1221 LogTags: nil, 1222 } 1223 keyBytes, err := md.getClient().GetKey(ctx, arg) 1224 if err != nil { 1225 return 1226 } 1227 1228 // decode the key 1229 err = md.config.Codec().Decode(keyBytes, &serverHalf) 1230 if err != nil { 1231 return 1232 } 1233 1234 return 1235 } 1236 1237 // PutTLFCryptKeyServerHalves is an implementation of the KeyServer interface. 1238 func (md *MDServerRemote) PutTLFCryptKeyServerHalves(ctx context.Context, 1239 keyServerHalves kbfsmd.UserDeviceKeyServerHalves) (err error) { 1240 ctx = rpc.WithFireNow(ctx) 1241 md.log.LazyTrace(ctx, "KeyServer: PutTLFCryptKeyServerHalves %v", keyServerHalves) 1242 defer func() { 1243 md.deferLog.LazyTrace(ctx, "KeyServer: PutTLFCryptKeyServerHalves %v (err=%v)", keyServerHalves, err) 1244 }() 1245 1246 // flatten out the map into an array 1247 var keyHalves []keybase1.KeyHalf 1248 for user, deviceMap := range keyServerHalves { 1249 for devicePubKey, serverHalf := range deviceMap { 1250 keyHalf, err := md.config.Codec().Encode(serverHalf) 1251 if err != nil { 1252 return err 1253 } 1254 keyHalves = append(keyHalves, 1255 keybase1.KeyHalf{ 1256 User: user, 1257 DeviceKID: devicePubKey.KID(), 1258 Key: keyHalf, 1259 }) 1260 } 1261 } 1262 // put the keys 1263 arg := keybase1.PutKeysArg{ 1264 KeyHalves: keyHalves, 1265 LogTags: nil, 1266 } 1267 return md.getClient().PutKeys(ctx, arg) 1268 } 1269 1270 // DeleteTLFCryptKeyServerHalf is an implementation of the KeyServer interface. 1271 func (md *MDServerRemote) DeleteTLFCryptKeyServerHalf(ctx context.Context, 1272 uid keybase1.UID, key kbfscrypto.CryptPublicKey, 1273 serverHalfID kbfscrypto.TLFCryptKeyServerHalfID) (err error) { 1274 ctx = rpc.WithFireNow(ctx) 1275 md.log.LazyTrace(ctx, "KeyServer: DeleteTLFCryptKeyServerHalf %s %s", uid, serverHalfID) 1276 defer func() { 1277 md.deferLog.LazyTrace(ctx, "KeyServer: DeleteTLFCryptKeyServerHalf %s %s done (err=%v)", uid, serverHalfID, err) 1278 }() 1279 1280 // encode the ID 1281 idBytes, err := md.config.Codec().Encode(serverHalfID) 1282 if err != nil { 1283 return err 1284 } 1285 1286 // get the key 1287 arg := keybase1.DeleteKeyArg{ 1288 Uid: uid, 1289 DeviceKID: key.KID(), 1290 KeyHalfID: idBytes, 1291 LogTags: nil, 1292 } 1293 err = md.getClient().DeleteKey(ctx, arg) 1294 if err != nil { 1295 return err 1296 } 1297 1298 return nil 1299 } 1300 1301 // DisableRekeyUpdatesForTesting implements the MDServer interface. 1302 func (md *MDServerRemote) DisableRekeyUpdatesForTesting() { 1303 // This doesn't need a lock for testing. 1304 md.squelchRekey = true 1305 md.rekeyTimer.Stop() 1306 } 1307 1308 // CtxMDSRTagKey is the type used for unique context tags within MDServerRemote 1309 type CtxMDSRTagKey int 1310 1311 const ( 1312 // CtxMDSRIDKey is the type of the tag for unique operation IDs 1313 // within MDServerRemote. 1314 CtxMDSRIDKey CtxMDSRTagKey = iota 1315 ) 1316 1317 // CtxMDSROpID is the display name for the unique operation 1318 // MDServerRemote ID tag. 1319 const CtxMDSROpID = "MDSRID" 1320 1321 func (md *MDServerRemote) backgroundRekeyChecker(ctx context.Context) { 1322 for { 1323 select { 1324 case <-md.rekeyTimer.C: 1325 if !md.getConn().IsConnected() { 1326 md.resetRekeyTimer() 1327 continue 1328 } 1329 1330 // Assign an ID to this rekey check so we can track it. 1331 newCtx := CtxWithRandomIDReplayable(ctx, CtxMDSRIDKey, CtxMDSROpID, md.log) 1332 md.log.CDebugf(newCtx, "Checking for rekey folders") 1333 if err := md.getFoldersForRekey( 1334 newCtx, md.getClient()); err != nil { 1335 md.log.CWarningf(newCtx, "MDServerRemote: getFoldersForRekey "+ 1336 "failed with %v", err) 1337 } 1338 md.resetRekeyTimer() 1339 case <-ctx.Done(): 1340 return 1341 } 1342 } 1343 } 1344 1345 // GetKeyBundles implements the MDServer interface for MDServerRemote. 1346 func (md *MDServerRemote) GetKeyBundles(ctx context.Context, 1347 tlf tlf.ID, wkbID kbfsmd.TLFWriterKeyBundleID, rkbID kbfsmd.TLFReaderKeyBundleID) ( 1348 wkb *kbfsmd.TLFWriterKeyBundleV3, rkb *kbfsmd.TLFReaderKeyBundleV3, err error) { 1349 ctx = rpc.WithFireNow(ctx) 1350 md.log.LazyTrace(ctx, "KeyServer: GetKeyBundles %s %s %s", tlf, wkbID, rkbID) 1351 defer func() { 1352 md.deferLog.LazyTrace(ctx, "KeyServer: GetKeyBundles %s %s %s done (err=%v)", tlf, wkbID, rkbID, err) 1353 }() 1354 1355 arg := keybase1.GetKeyBundlesArg{ 1356 FolderID: tlf.String(), 1357 WriterBundleID: wkbID.String(), 1358 ReaderBundleID: rkbID.String(), 1359 } 1360 1361 response, err := md.getClient().GetKeyBundles(ctx, arg) 1362 if err != nil { 1363 return nil, nil, err 1364 } 1365 1366 if response.WriterBundle.Bundle != nil { 1367 if response.WriterBundle.Version != int(kbfsmd.SegregatedKeyBundlesVer) { 1368 err = fmt.Errorf("Unsupported writer bundle version: %d", 1369 response.WriterBundle.Version) 1370 return nil, nil, err 1371 } 1372 wkb = new(kbfsmd.TLFWriterKeyBundleV3) 1373 err = md.config.Codec().Decode(response.WriterBundle.Bundle, wkb) 1374 if err != nil { 1375 return nil, nil, err 1376 } 1377 // Verify it's what we expect. 1378 bundleID, err := kbfsmd.MakeTLFWriterKeyBundleID(md.config.Codec(), *wkb) 1379 if err != nil { 1380 return nil, nil, err 1381 } 1382 if bundleID != wkbID { 1383 err = fmt.Errorf("Expected writer bundle ID %s, got: %s", 1384 wkbID, bundleID) 1385 return nil, nil, err 1386 } 1387 } 1388 1389 if response.ReaderBundle.Bundle != nil { 1390 if response.ReaderBundle.Version != int(kbfsmd.SegregatedKeyBundlesVer) { 1391 err = fmt.Errorf("Unsupported reader bundle version: %d", 1392 response.ReaderBundle.Version) 1393 return nil, nil, err 1394 } 1395 rkb = new(kbfsmd.TLFReaderKeyBundleV3) 1396 err = md.config.Codec().Decode(response.ReaderBundle.Bundle, rkb) 1397 if err != nil { 1398 return nil, nil, err 1399 } 1400 // Verify it's what we expect. 1401 bundleID, err := kbfsmd.MakeTLFReaderKeyBundleID(md.config.Codec(), *rkb) 1402 if err != nil { 1403 return nil, nil, err 1404 } 1405 if bundleID != rkbID { 1406 err = fmt.Errorf("Expected reader bundle ID %s, got: %s", 1407 rkbID, bundleID) 1408 return nil, nil, err 1409 } 1410 } 1411 1412 return wkb, rkb, nil 1413 } 1414 1415 // FastForwardBackoff implements the MDServer interface for MDServerRemote. 1416 func (md *MDServerRemote) FastForwardBackoff() { 1417 md.connMu.RLock() 1418 defer md.connMu.RUnlock() 1419 md.conn.FastForwardConnectDelayTimer() 1420 } 1421 1422 // FindNextMD implements the MDServer interface for MDServerRemote. 1423 func (md *MDServerRemote) FindNextMD( 1424 ctx context.Context, tlfID tlf.ID, rootSeqno keybase1.Seqno) ( 1425 nextKbfsRoot *kbfsmd.MerkleRoot, nextMerkleNodes [][]byte, 1426 nextRootSeqno keybase1.Seqno, err error) { 1427 ctx = rpc.WithFireNow(ctx) 1428 md.log.LazyTrace(ctx, "KeyServer: FindNextMD %s %d", tlfID, rootSeqno) 1429 md.log.CDebugf(ctx, "KeyServer: FindNextMD %s %d", tlfID, rootSeqno) 1430 defer func() { 1431 md.deferLog.LazyTrace(ctx, "KeyServer: FindNextMD %s %d done (err=%v)", 1432 tlfID, rootSeqno, err) 1433 md.deferLog.CDebugf(ctx, "KeyServer: FindNextMD %s %d done (err=%v)", 1434 tlfID, rootSeqno, err) 1435 }() 1436 1437 arg := keybase1.FindNextMDArg{ 1438 FolderID: tlfID.String(), 1439 Seqno: rootSeqno, 1440 } 1441 1442 response, err := md.getClient().FindNextMD(ctx, arg) 1443 if err != nil { 1444 return nil, nil, 0, err 1445 } 1446 1447 if len(response.MerkleNodes) == 0 { 1448 md.log.CDebugf(ctx, "No merkle data found for %s, seqno=%d", 1449 tlfID, rootSeqno) 1450 return nil, nil, 0, nil 1451 } 1452 1453 if response.KbfsRoot.Version != 1 { 1454 return nil, nil, 0, 1455 kbfsmd.NewMerkleVersionError{Version: response.KbfsRoot.Version} 1456 } 1457 1458 // Verify this is a valid merkle root and KBFS root before we 1459 // decode the bytes. 1460 md.log.CDebugf(ctx, "Verifying merkle root %d", response.RootSeqno) 1461 root := keybase1.MerkleRootV2{ 1462 Seqno: response.RootSeqno, 1463 HashMeta: response.RootHash, 1464 } 1465 expectedKbfsRoot := keybase1.KBFSRoot{ 1466 TreeID: tlfToMerkleTreeID(tlfID), 1467 Root: response.KbfsRoot.Root, 1468 } 1469 err = md.config.KBPKI().VerifyMerkleRoot(ctx, root, expectedKbfsRoot) 1470 if err != nil { 1471 return nil, nil, 0, err 1472 } 1473 1474 var kbfsRoot kbfsmd.MerkleRoot 1475 err = md.config.Codec().Decode(response.KbfsRoot.Root, &kbfsRoot) 1476 if err != nil { 1477 return nil, nil, 0, err 1478 } 1479 1480 // Validate the hashes of the nodes all the way down to the leaf. 1481 err = verifyMerkleNodes(ctx, &kbfsRoot, response.MerkleNodes, tlfID) 1482 if err != nil { 1483 return nil, nil, 0, err 1484 } 1485 1486 return &kbfsRoot, response.MerkleNodes, response.RootSeqno, nil 1487 } 1488 1489 // GetMerkleRootLatest implements the MDServer interface for MDServerRemote. 1490 func (md *MDServerRemote) GetMerkleRootLatest( 1491 ctx context.Context, treeID keybase1.MerkleTreeID) ( 1492 root *kbfsmd.MerkleRoot, err error) { 1493 ctx = rpc.WithFireNow(ctx) 1494 md.log.LazyTrace(ctx, "KeyServer: GetMerkleRootLatest %d", treeID) 1495 md.log.CDebugf(ctx, "KeyServer: GetMerkleRootLatest %d", treeID) 1496 defer func() { 1497 md.deferLog.LazyTrace(ctx, 1498 "KeyServer: GetMerkleRootLatest %d done (err=%v)", treeID, err) 1499 md.deferLog.CDebugf(ctx, 1500 "KeyServer: GetMerkleRootLatest %d done (err=%v)", treeID, err) 1501 }() 1502 1503 res, err := md.getClient().GetMerkleRootLatest(ctx, treeID) 1504 if err != nil { 1505 return nil, err 1506 } 1507 1508 if res.Version != 1 { 1509 return nil, kbfsmd.NewMerkleVersionError{Version: res.Version} 1510 } 1511 1512 var kbfsRoot kbfsmd.MerkleRoot 1513 err = md.config.Codec().Decode(res.Root, &kbfsRoot) 1514 if err != nil { 1515 return nil, err 1516 } 1517 1518 return &kbfsRoot, nil 1519 } 1520 1521 func (md *MDServerRemote) resetRekeyTimer() { 1522 md.rekeyTimer.Reset(nextRekeyTime()) 1523 } 1524 1525 // nextRekeyTime returns the time remaining to the next rekey. 1526 // The time returned is random with the formula: 1527 // MdServerBackgroundRekeyPeriod/2 + (k * (MdServerBackgroundRekeyPeriod/n)) 1528 // average: MdServerBackgroundRekeyPeriod 1529 // minimum: MdServerBackgroundRekeyPeriod/2 1530 // maximum: MdServerBackgroundRekeyPeriod*1.5 1531 // k=0..n, random uniformly distributed. 1532 func nextRekeyTime() time.Duration { 1533 var buf [1]byte 1534 err := kbfscrypto.RandRead(buf[:]) 1535 if err != nil { 1536 panic("nextRekeyTime: Random source broken!") 1537 } 1538 return (MdServerBackgroundRekeyPeriod / 2) + 1539 (time.Duration(buf[0]) * (MdServerBackgroundRekeyPeriod / 0xFF)) 1540 }