github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/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  }