github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/keybase_service_base.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 "strings" 10 "sync" 11 "time" 12 13 "github.com/keybase/client/go/kbfs/data" 14 "github.com/keybase/client/go/kbfs/favorites" 15 "github.com/keybase/client/go/kbfs/idutil" 16 "github.com/keybase/client/go/kbfs/kbfscrypto" 17 "github.com/keybase/client/go/kbfs/kbfsmd" 18 "github.com/keybase/client/go/kbfs/tlf" 19 "github.com/keybase/client/go/kbfs/tlfhandle" 20 kbname "github.com/keybase/client/go/kbun" 21 "github.com/keybase/client/go/libkb" 22 "github.com/keybase/client/go/logger" 23 "github.com/keybase/client/go/protocol/keybase1" 24 "github.com/pkg/errors" 25 "golang.org/x/net/context" 26 ) 27 28 const ( 29 cacheNotWriterExpiration = 5 * time.Second 30 ) 31 32 // KeybaseServiceBase implements most of KeybaseService from protocol 33 // defined clients. 34 type KeybaseServiceBase struct { 35 context Context 36 identifyClient keybase1.IdentifyInterface 37 userClient keybase1.UserInterface 38 teamsClient keybase1.TeamsInterface 39 merkleClient keybase1.MerkleInterface 40 sessionClient keybase1.SessionInterface 41 favoriteClient keybase1.FavoriteInterface 42 kbfsClient keybase1.KbfsInterface 43 kbfsMountClient keybase1.KbfsMountInterface 44 gitClient keybase1.GitInterface 45 kvstoreClient keybase1.KvstoreInterface 46 log logger.Logger 47 48 config Config 49 merkleRoot *EventuallyConsistentMerkleRoot 50 51 sessionCacheLock sync.RWMutex 52 // Set to the zero value when invalidated. 53 cachedCurrentSession idutil.SessionInfo 54 sessionInProgressCh chan struct{} 55 56 userCacheLock sync.RWMutex 57 // Map entries are removed when invalidated. 58 userCache map[keybase1.UID]idutil.UserInfo 59 60 teamCacheLock sync.RWMutex 61 // Map entries are removed when invalidated. 62 teamCache map[keybase1.TeamID]idutil.TeamInfo 63 notWriterCache map[keybase1.TeamID]map[keybase1.UID]time.Time 64 } 65 66 // Wrapper over `KeybaseServiceBase` implementing a `merkleRootGetter` 67 // that gets the merkle root directly from the service, without using 68 // the cache. 69 type keybaseServiceMerkleGetter struct { 70 k *KeybaseServiceBase 71 } 72 73 var _ idutil.MerkleRootGetter = (*keybaseServiceMerkleGetter)(nil) 74 75 func (k *keybaseServiceMerkleGetter) GetCurrentMerkleRoot( 76 ctx context.Context) (keybase1.MerkleRootV2, time.Time, error) { 77 return k.k.getCurrentMerkleRoot(ctx) 78 } 79 80 func (k *keybaseServiceMerkleGetter) VerifyMerkleRoot( 81 _ context.Context, _ keybase1.MerkleRootV2, _ keybase1.KBFSRoot) error { 82 panic("constMerkleRootGetter doesn't verify merkle roots") 83 } 84 85 // NewKeybaseServiceBase makes a new KeybaseService. 86 func NewKeybaseServiceBase(config Config, kbCtx Context, log logger.Logger) *KeybaseServiceBase { 87 k := KeybaseServiceBase{ 88 config: config, 89 context: kbCtx, 90 log: log, 91 userCache: make(map[keybase1.UID]idutil.UserInfo), 92 teamCache: make(map[keybase1.TeamID]idutil.TeamInfo), 93 notWriterCache: make(map[keybase1.TeamID]map[keybase1.UID]time.Time), 94 } 95 if config != nil { 96 k.merkleRoot = NewEventuallyConsistentMerkleRoot( 97 config, &keybaseServiceMerkleGetter{&k}) 98 } 99 return &k 100 } 101 102 // FillClients sets the client protocol implementations needed for a KeybaseService. 103 func (k *KeybaseServiceBase) FillClients( 104 identifyClient keybase1.IdentifyInterface, 105 userClient keybase1.UserInterface, teamsClient keybase1.TeamsInterface, 106 merkleClient keybase1.MerkleInterface, 107 sessionClient keybase1.SessionInterface, 108 favoriteClient keybase1.FavoriteInterface, 109 kbfsClient keybase1.KbfsInterface, 110 kbfsMountClient keybase1.KbfsMountInterface, 111 gitClient keybase1.GitInterface, kvstoreClient keybase1.KvstoreClient) { 112 k.identifyClient = identifyClient 113 k.userClient = userClient 114 k.teamsClient = teamsClient 115 k.merkleClient = merkleClient 116 k.sessionClient = sessionClient 117 k.favoriteClient = favoriteClient 118 k.kbfsClient = kbfsClient 119 k.kbfsMountClient = kbfsMountClient 120 k.gitClient = gitClient 121 k.kvstoreClient = kvstoreClient 122 } 123 124 type addVerifyingKeyFunc func(kbfscrypto.VerifyingKey) 125 type addCryptPublicKeyFunc func(kbfscrypto.CryptPublicKey) 126 127 // processKey adds the given public key to the appropriate verifying 128 // or crypt list (as return values), and also updates the given name 129 // map and parent map in place. 130 func processKey(publicKey keybase1.PublicKeyV2NaCl, 131 addVerifyingKey addVerifyingKeyFunc, 132 addCryptPublicKey addCryptPublicKeyFunc, 133 kidNames map[keybase1.KID]string, 134 parents map[keybase1.KID]keybase1.KID) error { 135 // Import the KID to validate it. 136 key, err := libkb.ImportKeypairFromKID(publicKey.Base.Kid) 137 if err != nil { 138 return err 139 } 140 if publicKey.Base.IsSibkey { 141 addVerifyingKey(kbfscrypto.MakeVerifyingKey(key.GetKID())) 142 } else { 143 addCryptPublicKey(kbfscrypto.MakeCryptPublicKey(key.GetKID())) 144 } 145 if publicKey.DeviceDescription != "" { 146 kidNames[publicKey.Base.Kid] = publicKey.DeviceDescription 147 } 148 149 if publicKey.Parent != nil { 150 parents[publicKey.Base.Kid] = *publicKey.Parent 151 } 152 return nil 153 } 154 155 // updateKIDNamesFromParents sets the name of each KID without a name 156 // that has a a parent with a name, to that parent's name. 157 func updateKIDNamesFromParents(kidNames map[keybase1.KID]string, 158 parents map[keybase1.KID]keybase1.KID) { 159 for kid, parent := range parents { 160 if _, ok := kidNames[kid]; ok { 161 continue 162 } 163 if parentName, ok := kidNames[parent]; ok { 164 kidNames[kid] = parentName 165 } 166 } 167 } 168 169 func filterKeys(keys map[keybase1.KID]keybase1.PublicKeyV2NaCl) ( 170 verifyingKeys []kbfscrypto.VerifyingKey, 171 cryptPublicKeys []kbfscrypto.CryptPublicKey, 172 kidNames map[keybase1.KID]string, err error) { 173 kidNames = make(map[keybase1.KID]string, len(keys)) 174 parents := make(map[keybase1.KID]keybase1.KID, len(keys)) 175 176 addVerifyingKey := func(key kbfscrypto.VerifyingKey) { 177 verifyingKeys = append(verifyingKeys, key) 178 } 179 addCryptPublicKey := func(key kbfscrypto.CryptPublicKey) { 180 cryptPublicKeys = append(cryptPublicKeys, key) 181 } 182 183 for _, publicKey := range keys { 184 if publicKey.Base.Revocation != nil { 185 continue 186 } 187 188 err := processKey(publicKey, addVerifyingKey, addCryptPublicKey, 189 kidNames, parents) 190 if err != nil { 191 return nil, nil, nil, err 192 } 193 } 194 updateKIDNamesFromParents(kidNames, parents) 195 return verifyingKeys, cryptPublicKeys, kidNames, nil 196 } 197 198 func (k *KeybaseServiceBase) filterRevokedKeys( 199 ctx context.Context, 200 uid keybase1.UID, 201 keys map[keybase1.KID]keybase1.PublicKeyV2NaCl, 202 reset *keybase1.ResetSummary) ( 203 map[kbfscrypto.VerifyingKey]idutil.RevokedKeyInfo, 204 map[kbfscrypto.CryptPublicKey]idutil.RevokedKeyInfo, 205 map[keybase1.KID]string, error) { 206 verifyingKeys := make(map[kbfscrypto.VerifyingKey]idutil.RevokedKeyInfo) 207 cryptPublicKeys := make( 208 map[kbfscrypto.CryptPublicKey]idutil.RevokedKeyInfo) 209 var kidNames = map[keybase1.KID]string{} 210 var parents = map[keybase1.KID]keybase1.KID{} 211 212 for _, key := range keys { 213 var info idutil.RevokedKeyInfo 214 switch { 215 case key.Base.Revocation != nil: 216 info.Time = key.Base.Revocation.Time 217 info.MerkleRoot = key.Base.Revocation.PrevMerkleRootSigned 218 // If we don't have a prev seqno, then we already have the 219 // best merkle data we're going to get. 220 info.SetFilledInMerkle(info.MerkleRoot.Seqno <= 0) 221 info.SetSigChainLocation(key.Base.Revocation.SigChainLocation) 222 case reset != nil: 223 info.Time = keybase1.ToTime(keybase1.FromUnixTime(reset.Ctime)) 224 info.MerkleRoot.Seqno = reset.MerkleRoot.Seqno 225 info.MerkleRoot.HashMeta = reset.MerkleRoot.HashMeta 226 // If we don't have a prev seqno, then we already have the 227 // best merkle data we're going to get. 228 info.SetFilledInMerkle(info.MerkleRoot.Seqno <= 0) 229 info.SetResetInfo(reset.ResetSeqno, true) 230 default: 231 // Not revoked. 232 continue 233 } 234 235 addVerifyingKey := func(key kbfscrypto.VerifyingKey) { 236 verifyingKeys[key] = info 237 } 238 addCryptPublicKey := func(key kbfscrypto.CryptPublicKey) { 239 cryptPublicKeys[key] = info 240 } 241 err := processKey(key, addVerifyingKey, addCryptPublicKey, 242 kidNames, parents) 243 if err != nil { 244 return nil, nil, nil, err 245 } 246 } 247 updateKIDNamesFromParents(kidNames, parents) 248 return verifyingKeys, cryptPublicKeys, kidNames, nil 249 250 } 251 252 func (k *KeybaseServiceBase) getCachedCurrentSession() idutil.SessionInfo { 253 k.sessionCacheLock.RLock() 254 defer k.sessionCacheLock.RUnlock() 255 return k.cachedCurrentSession 256 } 257 258 func (k *KeybaseServiceBase) setCachedCurrentSession(s idutil.SessionInfo) { 259 k.sessionCacheLock.Lock() 260 defer k.sessionCacheLock.Unlock() 261 k.cachedCurrentSession = s 262 } 263 264 func (k *KeybaseServiceBase) getCachedUserInfo( 265 uid keybase1.UID) idutil.UserInfo { 266 k.userCacheLock.RLock() 267 defer k.userCacheLock.RUnlock() 268 return k.userCache[uid] 269 } 270 271 func (k *KeybaseServiceBase) setCachedUserInfo( 272 uid keybase1.UID, info idutil.UserInfo) { 273 k.userCacheLock.Lock() 274 defer k.userCacheLock.Unlock() 275 if info.Name == kbname.NormalizedUsername("") { 276 delete(k.userCache, uid) 277 } else { 278 k.userCache[uid] = info 279 } 280 } 281 282 func (k *KeybaseServiceBase) getCachedTeamInfo( 283 tid keybase1.TeamID) idutil.TeamInfo { 284 k.teamCacheLock.RLock() 285 defer k.teamCacheLock.RUnlock() 286 return k.teamCache[tid] 287 } 288 289 func (k *KeybaseServiceBase) setCachedTeamInfo( 290 tid keybase1.TeamID, info idutil.TeamInfo) { 291 k.teamCacheLock.Lock() 292 defer k.teamCacheLock.Unlock() 293 if info.Name == kbname.NormalizedUsername("") { 294 delete(k.teamCache, tid) 295 delete(k.notWriterCache, tid) 296 } else { 297 k.teamCache[tid] = info 298 } 299 } 300 301 func (k *KeybaseServiceBase) getCachedNotWriter( 302 tid keybase1.TeamID, uid keybase1.UID) (notWriter bool) { 303 // Full write lock because of the delete-after-expiration code 304 // below. 305 k.teamCacheLock.Lock() 306 defer k.teamCacheLock.Unlock() 307 cachedTime, notWriter := k.notWriterCache[tid][uid] 308 if !notWriter { 309 return false 310 } 311 312 if k.config.Clock().Now().Sub(cachedTime) > cacheNotWriterExpiration { 313 delete(k.notWriterCache[tid], uid) 314 return false 315 } 316 return true 317 } 318 319 func (k *KeybaseServiceBase) setCachedNotWriter( 320 tid keybase1.TeamID, uid keybase1.UID) { 321 k.teamCacheLock.Lock() 322 defer k.teamCacheLock.Unlock() 323 teamMap := k.notWriterCache[tid] 324 if teamMap == nil { 325 teamMap = make(map[keybase1.UID]time.Time) 326 k.notWriterCache[tid] = teamMap 327 } 328 teamMap[uid] = k.config.Clock().Now() 329 } 330 331 // ClearCaches implements the KeybaseService interface for 332 // KeybaseServiceBase. 333 func (k *KeybaseServiceBase) ClearCaches(ctx context.Context) { 334 k.log.CDebugf(ctx, "Clearing KBFS-side user and team caches") 335 336 k.setCachedCurrentSession(idutil.SessionInfo{}) 337 func() { 338 k.userCacheLock.Lock() 339 defer k.userCacheLock.Unlock() 340 k.userCache = make(map[keybase1.UID]idutil.UserInfo) 341 }() 342 k.teamCacheLock.Lock() 343 defer k.teamCacheLock.Unlock() 344 k.teamCache = make(map[keybase1.TeamID]idutil.TeamInfo) 345 k.notWriterCache = make(map[keybase1.TeamID]map[keybase1.UID]time.Time) 346 } 347 348 // LoggedIn implements keybase1.NotifySessionInterface. 349 func (k *KeybaseServiceBase) LoggedIn(ctx context.Context, arg keybase1.LoggedInArg) error { 350 k.log.CDebugf(ctx, "Current session logged in: %s, signedUp: %t", arg.Username, arg.SignedUp) 351 // Since we don't have the whole session, just clear the cache and 352 // repopulate it. The `CurrentSession` call executes the "logged 353 // in" flow. 354 k.setCachedCurrentSession(idutil.SessionInfo{}) 355 const sessionID = 0 356 _, err := k.CurrentSession(ctx, sessionID) 357 if err != nil { 358 k.log.CDebugf(ctx, "Getting current session failed when %s is logged "+ 359 "in, so pretending user has logged out: %v", 360 arg.Username, err) 361 if k.config != nil { 362 serviceLoggedOut(ctx, k.config) 363 } 364 return nil 365 } 366 367 return nil 368 } 369 370 // LoggedOut implements keybase1.NotifySessionInterface. 371 func (k *KeybaseServiceBase) LoggedOut(ctx context.Context) error { 372 k.log.CDebugf(ctx, "Current session logged out") 373 k.setCachedCurrentSession(idutil.SessionInfo{}) 374 if k.config != nil { 375 serviceLoggedOut(ctx, k.config) 376 } 377 return nil 378 } 379 380 // KeyfamilyChanged implements keybase1.NotifyKeyfamilyInterface. 381 func (k *KeybaseServiceBase) KeyfamilyChanged(ctx context.Context, 382 uid keybase1.UID) error { 383 k.log.CDebugf(ctx, "Key family for user %s changed", uid) 384 k.setCachedUserInfo(uid, idutil.UserInfo{}) 385 386 if k.getCachedCurrentSession().UID == uid { 387 mdServer := k.config.MDServer() 388 if mdServer != nil { 389 // Ignore any errors for now, we don't want to block this 390 // notification and it's not worth spawning a goroutine for. 391 mdServer.CheckForRekeys(context.Background()) 392 } 393 } 394 395 return nil 396 } 397 398 // ReachabilityChanged implements keybase1.ReachabiltyInterface. 399 func (k *KeybaseServiceBase) ReachabilityChanged(ctx context.Context, 400 reachability keybase1.Reachability) error { 401 k.log.CDebugf(ctx, "CheckReachability invoked: %v", reachability) 402 if reachability.Reachable == keybase1.Reachable_YES { 403 k.config.KBFSOps().PushConnectionStatusChange(GregorServiceName, nil) 404 } else { 405 k.config.KBFSOps().PushConnectionStatusChange( 406 GregorServiceName, errDisconnected{}) 407 } 408 mdServer := k.config.MDServer() 409 if mdServer != nil { 410 mdServer.CheckReachability(ctx) 411 } 412 return nil 413 } 414 415 // StartReachability implements keybase1.ReachabilityInterface. 416 func (k *KeybaseServiceBase) StartReachability(ctx context.Context) (res keybase1.Reachability, err error) { 417 return k.CheckReachability(ctx) 418 } 419 420 // CheckReachability implements keybase1.ReachabilityInterface. 421 func (k *KeybaseServiceBase) CheckReachability(ctx context.Context) (res keybase1.Reachability, err error) { 422 res.Reachable = keybase1.Reachable_NO 423 mdServer := k.config.MDServer() 424 if mdServer != nil && mdServer.IsConnected() { 425 res.Reachable = keybase1.Reachable_YES 426 } 427 return res, nil 428 } 429 430 // PaperKeyCached implements keybase1.NotifyPaperKeyInterface. 431 func (k *KeybaseServiceBase) PaperKeyCached(ctx context.Context, 432 arg keybase1.PaperKeyCachedArg) error { 433 k.log.CDebugf(ctx, "Paper key for %s cached", arg.Uid) 434 435 if k.getCachedCurrentSession().UID == arg.Uid { 436 err := k.config.KBFSOps().KickoffAllOutstandingRekeys() 437 if err != nil { 438 // Ignore and log errors here. For now the only way it could error 439 // is when the method is called on a folderBranchOps which is a 440 // developer mistake and not recoverable from code. 441 k.log.CDebugf(ctx, 442 "Calling KickoffAllOutstandingRekeys error: %s", err) 443 } 444 // Ignore any errors for now, we don't want to block this 445 // notification and it's not worth spawning a goroutine for. 446 mdServer := k.config.MDServer() 447 if mdServer != nil { 448 mdServer.CheckForRekeys(context.Background()) 449 } 450 } 451 452 return nil 453 } 454 455 // ClientOutOfDate implements keybase1.NotifySessionInterface. 456 func (k *KeybaseServiceBase) ClientOutOfDate(ctx context.Context, 457 arg keybase1.ClientOutOfDateArg) error { 458 k.log.CDebugf(ctx, "Client out of date: %v", arg) 459 return nil 460 } 461 462 // RootAuditError implements keybase1.NotifyAuditInterface. 463 func (k *KeybaseServiceBase) RootAuditError(ctx context.Context, 464 arg keybase1.RootAuditErrorArg) error { 465 k.log.CDebugf(ctx, "Merkle tree audit error: %v", arg.Message) 466 return nil 467 } 468 469 // ConvertIdentifyError converts a errors during identify into KBFS errors 470 func ConvertIdentifyError(assertion string, err error) error { 471 switch err.(type) { 472 case libkb.NotFoundError: 473 return idutil.NoSuchUserError{Input: assertion} 474 case libkb.ResolutionError: 475 return idutil.NoSuchUserError{Input: assertion} 476 } 477 return err 478 } 479 480 // Resolve implements the KeybaseService interface for KeybaseServiceBase. 481 func (k *KeybaseServiceBase) Resolve( 482 ctx context.Context, assertion string, 483 offline keybase1.OfflineAvailability) ( 484 kbname.NormalizedUsername, keybase1.UserOrTeamID, error) { 485 res, err := k.identifyClient.Resolve3( 486 ctx, keybase1.Resolve3Arg{ 487 Assertion: assertion, 488 Oa: offline, 489 }) 490 if err != nil { 491 return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), 492 ConvertIdentifyError(assertion, err) 493 } 494 return kbname.NewNormalizedUsername(res.Name), res.Id, nil 495 } 496 497 // Identify implements the KeybaseService interface for KeybaseServiceBase. 498 func (k *KeybaseServiceBase) Identify( 499 ctx context.Context, assertion, reason string, 500 offline keybase1.OfflineAvailability) ( 501 kbname.NormalizedUsername, keybase1.UserOrTeamID, error) { 502 // setting UseDelegateUI to true here will cause daemon to use 503 // registered identify ui providers instead of terminal if any 504 // are available. If not, then it will use the terminal UI. 505 arg := keybase1.IdentifyLiteArg{ 506 Assertion: assertion, 507 UseDelegateUI: true, 508 Reason: keybase1.IdentifyReason{Reason: reason}, 509 // No need to go back and forth with the UI until the service 510 // knows for sure there's a need for a dialogue. 511 CanSuppressUI: true, 512 Oa: offline, 513 } 514 515 ei := tlfhandle.GetExtendedIdentify(ctx) 516 arg.IdentifyBehavior = ei.Behavior 517 518 res, err := k.identifyClient.IdentifyLite(ctx, arg) 519 // IdentifyLite still returns keybase1.UserPlusKeys data (sans 520 // keys), even if it gives a NoSigChainError or a UserDeletedError, 521 // and in KBFS it's fine if the user doesn't have a full sigchain 522 // (e.g., it's just like the sharing before signup case, except 523 // the user already has a UID). Both types of users are based 524 // entirely on server trust anyway. 525 switch err.(type) { 526 case nil: 527 case libkb.NoSigChainError, libkb.UserDeletedError: 528 ei.OnError(ctx) 529 // But if the username is blame, just return it, since the 530 // returned username would be useless and confusing. 531 if res.Ul.Name == "" { 532 return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err 533 } 534 k.log.CDebugf(ctx, 535 "Ignoring error (%s) for user %s with no sigchain; "+ 536 "error type=%T", err, res.Ul.Name, err) 537 default: 538 // If the caller is waiting for breaks, let them know we got an error. 539 ei.OnError(ctx) 540 return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), 541 ConvertIdentifyError(assertion, err) 542 } 543 544 // This is required for every identify call. The userBreak 545 // function will take care of checking if res.TrackBreaks is nil 546 // or not. 547 name := kbname.NormalizedUsername(res.Ul.Name) 548 if res.Ul.Id.IsUser() { 549 asUser, err := res.Ul.Id.AsUser() 550 if err != nil { 551 return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err 552 } 553 ei.UserBreak(ctx, name, asUser, res.TrackBreaks) 554 } else if !res.Ul.Id.IsNil() { 555 ei.TeamBreak(ctx, res.Ul.Id.AsTeamOrBust(), res.TrackBreaks) 556 } 557 558 return name, res.Ul.Id, nil 559 } 560 561 // NormalizeSocialAssertion implements the KeybaseService interface for 562 // KeybaseServiceBase. 563 func (k *KeybaseServiceBase) NormalizeSocialAssertion( 564 ctx context.Context, assertion string) (keybase1.SocialAssertion, error) { 565 return k.identifyClient.NormalizeSocialAssertion(ctx, assertion) 566 } 567 568 // ResolveIdentifyImplicitTeam implements the KeybaseService interface 569 // for KeybaseServiceBase. 570 func (k *KeybaseServiceBase) ResolveIdentifyImplicitTeam( 571 ctx context.Context, assertions, suffix string, tlfType tlf.Type, 572 doIdentifies bool, reason string, 573 offline keybase1.OfflineAvailability) (idutil.ImplicitTeamInfo, error) { 574 if tlfType != tlf.Private && tlfType != tlf.Public { 575 return idutil.ImplicitTeamInfo{}, fmt.Errorf( 576 "Invalid implicit team TLF type: %s", tlfType) 577 } 578 579 arg := keybase1.ResolveIdentifyImplicitTeamArg{ 580 Assertions: assertions, 581 Suffix: suffix, 582 DoIdentifies: doIdentifies, 583 Reason: keybase1.IdentifyReason{Reason: reason}, 584 Create: true, 585 IsPublic: tlfType == tlf.Public, 586 Oa: offline, 587 } 588 589 ei := tlfhandle.GetExtendedIdentify(ctx) 590 arg.IdentifyBehavior = ei.Behavior 591 592 res, err := k.identifyClient.ResolveIdentifyImplicitTeam(ctx, arg) 593 if err != nil { 594 return idutil.ImplicitTeamInfo{}, ConvertIdentifyError(assertions, err) 595 } 596 if strings.Contains(res.DisplayName, "_implicit_team_") { 597 k.log.CWarningf( 598 ctx, "Got display name %s for assertions %s", 599 res.DisplayName, assertions) 600 } 601 name := kbname.NormalizedUsername(res.DisplayName) 602 603 // Exactly one break callback is required for every identify call. 604 if doIdentifies { 605 if len(res.TrackBreaks) > 0 { 606 // Iterate the map to get one entry, then break. 607 for userVer, breaks := range res.TrackBreaks { 608 // TODO: resolve the UID into a username so we don't have to 609 // pass in the full display name here? 610 ei.UserBreak(ctx, name, userVer.Uid, &breaks) 611 break 612 } 613 } else { 614 ei.TeamBreak(ctx, keybase1.TeamID(""), nil) 615 } 616 } 617 618 iteamInfo := idutil.ImplicitTeamInfo{ 619 Name: name, 620 TID: res.TeamID, 621 } 622 if res.FolderID != "" { 623 iteamInfo.TlfID, err = tlf.ParseID(res.FolderID.String()) 624 if err != nil { 625 return idutil.ImplicitTeamInfo{}, err 626 } 627 } 628 629 return iteamInfo, nil 630 } 631 632 // ResolveImplicitTeamByID implements the KeybaseService interface for 633 // KeybaseServiceBase. 634 func (k *KeybaseServiceBase) ResolveImplicitTeamByID( 635 ctx context.Context, teamID keybase1.TeamID) (name string, err error) { 636 arg := keybase1.ResolveImplicitTeamArg{ 637 Id: teamID, 638 } 639 640 res, err := k.identifyClient.ResolveImplicitTeam(ctx, arg) 641 if err != nil { 642 return "", err 643 } 644 return res.Name, nil 645 } 646 647 func (k *KeybaseServiceBase) checkForRevokedVerifyingKey( 648 ctx context.Context, currUserInfo idutil.UserInfo, kid keybase1.KID) ( 649 newUserInfo idutil.UserInfo, exists bool, err error) { 650 newUserInfo = currUserInfo 651 for key, info := range currUserInfo.RevokedVerifyingKeys { 652 if !key.KID().Equal(kid) { 653 continue 654 } 655 exists = true 656 if info.FilledInMerkle() { 657 break 658 } 659 660 k.log.CDebugf(ctx, "Filling in merkle info for user %s, revoked key %s", 661 currUserInfo.UID, kid) 662 663 // If possible, ask the service to give us the first merkle 664 // root that covers this revoke. Some older device revokes 665 // didn't yet include a prev field, so we can't refine the 666 // merkle root in those cases, and will be relying only on 667 // server trust. 668 if info.MerkleRoot.Seqno > 0 { 669 var res keybase1.NextMerkleRootRes 670 resetSeqno, isReset := info.ResetInfo() 671 if isReset { 672 res, err = k.userClient.FindNextMerkleRootAfterReset(ctx, 673 keybase1.FindNextMerkleRootAfterResetArg{ 674 Uid: currUserInfo.UID, 675 ResetSeqno: resetSeqno, 676 Prev: keybase1.ResetMerkleRoot{ 677 Seqno: info.MerkleRoot.Seqno, 678 HashMeta: info.MerkleRoot.HashMeta, 679 }, 680 }) 681 } else { 682 res, err = k.userClient.FindNextMerkleRootAfterRevoke(ctx, 683 keybase1.FindNextMerkleRootAfterRevokeArg{ 684 Uid: currUserInfo.UID, 685 Kid: kid, 686 Loc: info.SigChainLocation(), 687 Prev: info.MerkleRoot, 688 }) 689 } 690 if m, ok := err.(libkb.MerkleClientError); ok && m.IsOldTree() { // nolint 691 k.log.CDebugf(ctx, "Merkle root is too old for checking "+ 692 "the revoked key: %+v", err) 693 info.MerkleRoot.Seqno = 0 694 } else if err != nil { 695 return idutil.UserInfo{}, false, err 696 } else if res.Res != nil { 697 info.MerkleRoot = *res.Res 698 } 699 } 700 info.SetFilledInMerkle(true) 701 newUserInfo = currUserInfo.DeepCopy() 702 newUserInfo.RevokedVerifyingKeys[key] = info 703 k.setCachedUserInfo(newUserInfo.UID, newUserInfo) 704 break 705 } 706 707 return newUserInfo, exists, nil 708 } 709 710 // LoadUserPlusKeys implements the KeybaseService interface for 711 // KeybaseServiceBase. 712 func (k *KeybaseServiceBase) LoadUserPlusKeys( 713 ctx context.Context, uid keybase1.UID, pollForKID keybase1.KID, 714 offline keybase1.OfflineAvailability) (idutil.UserInfo, error) { 715 cachedUserInfo := k.getCachedUserInfo(uid) 716 if cachedUserInfo.Name != kbname.NormalizedUsername("") { 717 if pollForKID == keybase1.KID("") { 718 return cachedUserInfo, nil 719 } 720 // Skip the cache if pollForKID isn't present in 721 // `VerifyingKeys` or one of the revoked verifying keys. 722 for _, key := range cachedUserInfo.VerifyingKeys { 723 if key.KID().Equal(pollForKID) { 724 return cachedUserInfo, nil 725 } 726 } 727 728 // Check if the key is revoked, and fill in the merkle info in 729 // that case. 730 cachedUserInfo, exists, err := k.checkForRevokedVerifyingKey( 731 ctx, cachedUserInfo, pollForKID) 732 if err != nil { 733 return idutil.UserInfo{}, err 734 } 735 if exists { 736 return cachedUserInfo, nil 737 } 738 } 739 740 arg := keybase1.LoadUserPlusKeysV2Arg{ 741 Uid: uid, 742 PollForKID: pollForKID, 743 Oa: offline, 744 } 745 res, err := k.userClient.LoadUserPlusKeysV2(ctx, arg) 746 if err != nil { 747 return idutil.UserInfo{}, err 748 } 749 750 userInfo, err := k.processUserPlusKeys(ctx, res) 751 if err != nil { 752 return idutil.UserInfo{}, err 753 } 754 755 if pollForKID != keybase1.KID("") { 756 // Fill in merkle info if we were explicitly trying to load a 757 // revoked key. 758 userInfo, _, err = k.checkForRevokedVerifyingKey( 759 ctx, userInfo, pollForKID) 760 if err != nil { 761 return idutil.UserInfo{}, err 762 } 763 } 764 return userInfo, nil 765 } 766 767 func (k *KeybaseServiceBase) getLastWriterInfo( 768 ctx context.Context, teamInfo idutil.TeamInfo, tlfType tlf.Type, 769 user keybase1.UID, verifyingKey kbfscrypto.VerifyingKey) ( 770 idutil.TeamInfo, error) { 771 if _, ok := teamInfo.LastWriters[verifyingKey]; ok { 772 // Already cached, nothing to do. 773 return teamInfo, nil 774 } 775 776 res, err := k.teamsClient.FindNextMerkleRootAfterTeamRemovalBySigningKey( 777 ctx, keybase1.FindNextMerkleRootAfterTeamRemovalBySigningKeyArg{ 778 Uid: user, 779 SigningKey: verifyingKey.KID(), 780 Team: teamInfo.TID, 781 IsPublic: tlfType == tlf.Public, 782 }) 783 if err != nil { 784 return idutil.TeamInfo{}, err 785 } 786 787 // Copy any old data to avoid races. 788 newLastWriters := make( 789 map[kbfscrypto.VerifyingKey]keybase1.MerkleRootV2, 790 len(teamInfo.LastWriters)+1) 791 for k, v := range teamInfo.LastWriters { 792 newLastWriters[k] = v 793 } 794 newLastWriters[verifyingKey] = *res.Res 795 teamInfo.LastWriters = newLastWriters 796 return teamInfo, nil 797 } 798 799 var allowedLoadTeamRoles = map[keybase1.TeamRole]bool{ 800 keybase1.TeamRole_NONE: true, 801 keybase1.TeamRole_WRITER: true, 802 keybase1.TeamRole_READER: true, 803 } 804 805 // LoadTeamPlusKeys implements the KeybaseService interface for 806 // KeybaseServiceBase. 807 func (k *KeybaseServiceBase) LoadTeamPlusKeys( 808 ctx context.Context, tid keybase1.TeamID, tlfType tlf.Type, 809 desiredKeyGen kbfsmd.KeyGen, desiredUser keybase1.UserVersion, 810 desiredKey kbfscrypto.VerifyingKey, desiredRole keybase1.TeamRole, 811 offline keybase1.OfflineAvailability) (idutil.TeamInfo, error) { 812 if !allowedLoadTeamRoles[desiredRole] { 813 panic(fmt.Sprintf("Disallowed team role: %v", desiredRole)) 814 } 815 816 cachedTeamInfo := k.getCachedTeamInfo(tid) 817 if cachedTeamInfo.Name != kbname.NormalizedUsername("") { 818 // If the cached team info doesn't satisfy our desires, don't 819 // use it. 820 satisfiesDesires := true 821 if desiredKeyGen >= kbfsmd.FirstValidKeyGen { 822 // If `desiredKeyGen` is at most as large as the keygen in 823 // the cached latest team info, then our cached info 824 // satisfies our desires. 825 satisfiesDesires = desiredKeyGen <= cachedTeamInfo.LatestKeyGen 826 } 827 828 if satisfiesDesires && desiredUser.Uid.Exists() { 829 // If the user is in the writer map, that satisfies none, reader 830 // or writer desires. 831 satisfiesDesires = cachedTeamInfo.Writers[desiredUser.Uid] 832 if !satisfiesDesires { 833 if desiredRole == keybase1.TeamRole_NONE || 834 desiredRole == keybase1.TeamRole_READER { 835 // If the user isn't a writer, but the desired 836 // role is a reader, we need to check the reader 837 // map explicitly. 838 satisfiesDesires = cachedTeamInfo.Readers[desiredUser.Uid] 839 } else { 840 if !desiredKey.IsNil() { 841 // If the desired role was at least a writer, but 842 // the user isn't currently a writer, see if they 843 // ever were. 844 var err error 845 cachedTeamInfo, err = k.getLastWriterInfo( 846 ctx, cachedTeamInfo, tlfType, desiredUser.Uid, 847 desiredKey) 848 if err != nil { 849 return idutil.TeamInfo{}, err 850 } 851 k.setCachedTeamInfo(tid, cachedTeamInfo) 852 } 853 854 // If we have recently learned that the user is 855 // not a writer (e.g., of a public folder), we 856 // should rely on that cached info to avoid 857 // looking that up too often. 858 satisfiesDesires = k.getCachedNotWriter( 859 tid, desiredUser.Uid) 860 } 861 } 862 } 863 864 if satisfiesDesires { 865 return cachedTeamInfo, nil 866 } 867 } 868 869 arg := keybase1.LoadTeamPlusApplicationKeysArg{ 870 Id: tid, 871 Application: keybase1.TeamApplication_KBFS, 872 IncludeKBFSKeys: true, 873 Oa: offline, 874 } 875 876 if desiredKeyGen >= kbfsmd.FirstValidKeyGen { 877 arg.Refreshers.NeedApplicationsAtGenerationsWithKBFS = 878 map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication{ 879 keybase1.PerTeamKeyGeneration(desiredKeyGen): { 880 keybase1.TeamApplication_KBFS, 881 }, 882 } 883 } 884 885 if desiredUser.Uid.Exists() && desiredKey.IsNil() { 886 arg.Refreshers.WantMembers = append( 887 arg.Refreshers.WantMembers, desiredUser) 888 arg.Refreshers.WantMembersRole = desiredRole 889 } 890 891 res, err := k.teamsClient.LoadTeamPlusApplicationKeys(ctx, arg) 892 if err != nil { 893 return idutil.TeamInfo{}, err 894 } 895 896 if tid != res.Id { 897 return idutil.TeamInfo{}, fmt.Errorf( 898 "TID doesn't match: %s vs %s", tid, res.Id) 899 } 900 901 info := idutil.TeamInfo{ 902 Name: kbname.NormalizedUsername(res.Name), 903 TID: res.Id, 904 CryptKeys: make(map[kbfsmd.KeyGen]kbfscrypto.TLFCryptKey), 905 Writers: make(map[keybase1.UID]bool), 906 Readers: make(map[keybase1.UID]bool), 907 } 908 for _, key := range res.ApplicationKeys { 909 keyGen := kbfsmd.KeyGen(key.KeyGeneration) 910 info.CryptKeys[keyGen] = 911 kbfscrypto.MakeTLFCryptKey(key.Key) 912 if keyGen > info.LatestKeyGen { 913 info.LatestKeyGen = keyGen 914 } 915 } 916 917 for _, user := range res.Writers { 918 info.Writers[user.Uid] = true 919 } 920 for _, user := range res.OnlyReaders { 921 info.Readers[user.Uid] = true 922 } 923 924 // For subteams, get the root team ID. 925 if tid.IsSubTeam() { 926 rootID, err := k.teamsClient.GetTeamRootID(ctx, tid) 927 if err != nil { 928 return idutil.TeamInfo{}, err 929 } 930 info.RootID = rootID 931 } 932 933 // Fill in `LastWriters`, only if needed. 934 if desiredUser.Uid.Exists() && desiredRole == keybase1.TeamRole_WRITER && 935 !info.Writers[desiredUser.Uid] && !desiredKey.IsNil() { 936 info, err = k.getLastWriterInfo( 937 ctx, info, tlfType, desiredUser.Uid, desiredKey) 938 if err != nil { 939 return idutil.TeamInfo{}, err 940 } 941 } 942 943 k.setCachedTeamInfo(tid, info) 944 945 if desiredUser.Uid.Exists() && !info.Writers[desiredUser.Uid] && 946 !(desiredRole == keybase1.TeamRole_NONE || 947 desiredRole == keybase1.TeamRole_READER) { 948 // Remember that this user was not a writer for a short 949 // amount of time, to avoid repeated lookups for writers 950 // in a public folder (for example). 951 k.setCachedNotWriter(tid, desiredUser.Uid) 952 } 953 954 return info, nil 955 } 956 957 // CreateTeamTLF implements the KeybaseService interface for 958 // KeybaseServiceBase. 959 func (k *KeybaseServiceBase) CreateTeamTLF( 960 ctx context.Context, teamID keybase1.TeamID, tlfID tlf.ID) (err error) { 961 return k.kbfsClient.CreateTLF(ctx, keybase1.CreateTLFArg{ 962 TeamID: teamID, 963 TlfID: keybase1.TLFID(tlfID.String()), 964 }) 965 } 966 967 // GetTeamSettings implements the KeybaseService interface for 968 // KeybaseServiceBase. 969 func (k *KeybaseServiceBase) GetTeamSettings( 970 ctx context.Context, teamID keybase1.TeamID, 971 offline keybase1.OfflineAvailability) ( 972 keybase1.KBFSTeamSettings, error) { 973 // TODO: get invalidations from the server and cache the settings? 974 return k.kbfsClient.GetKBFSTeamSettings( 975 ctx, keybase1.GetKBFSTeamSettingsArg{ 976 TeamID: teamID, 977 Oa: offline, 978 }) 979 } 980 981 func (k *KeybaseServiceBase) getCurrentMerkleRoot(ctx context.Context) ( 982 keybase1.MerkleRootV2, time.Time, error) { 983 const merkleFreshnessMs = int(time.Second * 60 / time.Millisecond) 984 res, err := k.merkleClient.GetCurrentMerkleRoot(ctx, merkleFreshnessMs) 985 if err != nil { 986 return keybase1.MerkleRootV2{}, time.Time{}, err 987 } 988 989 return res.Root, keybase1.FromTime(res.UpdateTime), nil 990 } 991 992 // GetCurrentMerkleRoot implements the KeybaseService interface for 993 // KeybaseServiceBase. 994 func (k *KeybaseServiceBase) GetCurrentMerkleRoot(ctx context.Context) ( 995 keybase1.MerkleRootV2, time.Time, error) { 996 // Refresh the cached value in the background if the cached value 997 // is older than 30s; if our cached value is more than 60s old, 998 // block. 999 _, root, rootTime, err := k.merkleRoot.Get( 1000 ctx, 30*time.Second, 60*time.Second) 1001 return root, rootTime, err 1002 } 1003 1004 // VerifyMerkleRoot implements the KBPKI interface for KeybaseServiceBase. 1005 func (k *KeybaseServiceBase) VerifyMerkleRoot( 1006 ctx context.Context, root keybase1.MerkleRootV2, 1007 kbfsRoot keybase1.KBFSRoot) error { 1008 return k.merkleClient.VerifyMerkleRootAndKBFS(ctx, 1009 keybase1.VerifyMerkleRootAndKBFSArg{ 1010 Root: root, 1011 ExpectedKBFSRoot: kbfsRoot, 1012 }) 1013 } 1014 1015 func (k *KeybaseServiceBase) processUserPlusKeys( 1016 ctx context.Context, upk keybase1.UserPlusKeysV2AllIncarnations) ( 1017 idutil.UserInfo, error) { 1018 verifyingKeys, cryptPublicKeys, kidNames, err := filterKeys( 1019 upk.Current.DeviceKeys) 1020 if err != nil { 1021 return idutil.UserInfo{}, err 1022 } 1023 1024 revokedVerifyingKeys, revokedCryptPublicKeys, revokedKidNames, err := 1025 k.filterRevokedKeys( 1026 ctx, upk.Current.Uid, upk.Current.DeviceKeys, upk.Current.Reset) 1027 if err != nil { 1028 return idutil.UserInfo{}, err 1029 } 1030 1031 if len(revokedKidNames) > 0 { 1032 for k, v := range revokedKidNames { 1033 kidNames[k] = v 1034 } 1035 } 1036 1037 for _, incarnation := range upk.PastIncarnations { 1038 revokedVerifyingKeysPast, revokedCryptPublicKeysPast, 1039 revokedKidNames, err := 1040 k.filterRevokedKeys( 1041 ctx, incarnation.Uid, incarnation.DeviceKeys, incarnation.Reset) 1042 if err != nil { 1043 return idutil.UserInfo{}, err 1044 } 1045 1046 if len(revokedKidNames) > 0 { 1047 for k, v := range revokedKidNames { 1048 kidNames[k] = v 1049 } 1050 } 1051 1052 for k, v := range revokedVerifyingKeysPast { 1053 revokedVerifyingKeys[k] = v 1054 } 1055 for k, v := range revokedCryptPublicKeysPast { 1056 revokedCryptPublicKeys[k] = v 1057 } 1058 } 1059 1060 u := idutil.UserInfo{ 1061 Name: kbname.NewNormalizedUsername( 1062 upk.Current.Username), 1063 UID: upk.Current.Uid, 1064 VerifyingKeys: verifyingKeys, 1065 CryptPublicKeys: cryptPublicKeys, 1066 KIDNames: kidNames, 1067 EldestSeqno: upk.Current.EldestSeqno, 1068 RevokedVerifyingKeys: revokedVerifyingKeys, 1069 RevokedCryptPublicKeys: revokedCryptPublicKeys, 1070 } 1071 1072 k.setCachedUserInfo(upk.Current.Uid, u) 1073 return u, nil 1074 } 1075 1076 func (k *KeybaseServiceBase) getCachedCurrentSessionOrInProgressCh() ( 1077 cachedSession idutil.SessionInfo, inProgressCh chan struct{}, doRPC bool) { 1078 k.sessionCacheLock.Lock() 1079 defer k.sessionCacheLock.Unlock() 1080 1081 if k.cachedCurrentSession != (idutil.SessionInfo{}) { 1082 return k.cachedCurrentSession, nil, false 1083 } 1084 1085 // If someone already started the RPC, wait for them (and release 1086 // the lock). 1087 if k.sessionInProgressCh != nil { 1088 return idutil.SessionInfo{}, k.sessionInProgressCh, false 1089 } 1090 1091 k.sessionInProgressCh = make(chan struct{}) 1092 return idutil.SessionInfo{}, k.sessionInProgressCh, true 1093 } 1094 1095 func (k *KeybaseServiceBase) getCurrentSession( 1096 ctx context.Context, sessionID int) (idutil.SessionInfo, bool, error) { 1097 var cachedCurrentSession idutil.SessionInfo 1098 var inProgressCh chan struct{} 1099 doRPC := false 1100 // Loop until either we have the session info, or until we are the 1101 // sole goroutine that needs to make the RPC. Avoid holding the 1102 // session cache lock during the RPC, since that can result in a 1103 // deadlock if the RPC results in a call to `ClearCaches()`. 1104 for !doRPC { 1105 cachedCurrentSession, inProgressCh, doRPC = 1106 k.getCachedCurrentSessionOrInProgressCh() 1107 if cachedCurrentSession != (idutil.SessionInfo{}) { 1108 return cachedCurrentSession, false, nil 1109 } 1110 1111 if !doRPC { 1112 // Wait for another goroutine to finish the RPC. 1113 select { 1114 case <-inProgressCh: 1115 case <-ctx.Done(): 1116 return idutil.SessionInfo{}, false, ctx.Err() 1117 } 1118 } 1119 } 1120 1121 var s idutil.SessionInfo 1122 // Close and clear the in-progress channel, even on an error. 1123 defer func() { 1124 k.sessionCacheLock.Lock() 1125 defer k.sessionCacheLock.Unlock() 1126 k.cachedCurrentSession = s 1127 close(k.sessionInProgressCh) 1128 k.sessionInProgressCh = nil 1129 }() 1130 1131 res, err := k.sessionClient.CurrentSession(ctx, sessionID) 1132 if err != nil { 1133 if _, ok := err.(libkb.NoSessionError); ok { 1134 // Use an error with a proper OS error code attached to it. 1135 err = idutil.NoCurrentSessionError{} 1136 } 1137 return idutil.SessionInfo{}, false, err 1138 } 1139 s, err = idutil.SessionInfoFromProtocol(res) 1140 if err != nil { 1141 return idutil.SessionInfo{}, false, err 1142 } 1143 1144 k.log.CDebugf( 1145 ctx, "new session with username %s, uid %s, crypt public key %s, and verifying key %s", 1146 s.Name, s.UID, s.CryptPublicKey, s.VerifyingKey) 1147 return s, true, nil 1148 } 1149 1150 // CurrentSession implements the KeybaseService interface for KeybaseServiceBase. 1151 func (k *KeybaseServiceBase) CurrentSession( 1152 ctx context.Context, sessionID int) ( 1153 idutil.SessionInfo, error) { 1154 ctx = CtxWithRandomIDReplayable( 1155 ctx, CtxKeybaseServiceIDKey, CtxKeybaseServiceOpID, k.log) 1156 1157 s, newSession, err := k.getCurrentSession(ctx, sessionID) 1158 if err != nil { 1159 return idutil.SessionInfo{}, err 1160 } 1161 1162 if newSession && k.config != nil { 1163 // Don't hold the lock while calling `serviceLoggedIn`. 1164 _ = serviceLoggedIn(ctx, k.config, s, TLFJournalBackgroundWorkEnabled) 1165 } 1166 1167 return s, nil 1168 } 1169 1170 // FavoriteAdd implements the KeybaseService interface for KeybaseServiceBase. 1171 func (k *KeybaseServiceBase) FavoriteAdd(ctx context.Context, folder keybase1.FolderHandle) error { 1172 return k.favoriteClient.FavoriteAdd(ctx, keybase1.FavoriteAddArg{Folder: folder}) 1173 } 1174 1175 // FavoriteDelete implements the KeybaseService interface for KeybaseServiceBase. 1176 func (k *KeybaseServiceBase) FavoriteDelete(ctx context.Context, folder keybase1.FolderHandle) error { 1177 return k.favoriteClient.FavoriteIgnore(ctx, 1178 keybase1.FavoriteIgnoreArg{Folder: folder}) 1179 } 1180 1181 // FavoriteList implements the KeybaseService interface for KeybaseServiceBase. 1182 func (k *KeybaseServiceBase) FavoriteList(ctx context.Context, 1183 sessionID int) (keybase1.FavoritesResult, error) { 1184 return k.favoriteClient.GetFavorites(ctx, sessionID) 1185 } 1186 1187 // EncryptFavorites encrypts cached favorites to store on disk. 1188 func (k *KeybaseServiceBase) EncryptFavorites(ctx context.Context, dataToEncrypt []byte) (res []byte, err error) { 1189 return k.kbfsClient.EncryptFavorites(ctx, dataToEncrypt) 1190 } 1191 1192 // DecryptFavorites decrypts cached favorites stored on disk. 1193 func (k *KeybaseServiceBase) DecryptFavorites(ctx context.Context, dataToEncrypt []byte) (res []byte, err error) { 1194 return k.kbfsClient.DecryptFavorites(ctx, dataToEncrypt) 1195 } 1196 1197 // NotifyOnlineStatusChanged implements the KeybaseService interface for 1198 // KeybaseServiceBase. 1199 func (k *KeybaseServiceBase) NotifyOnlineStatusChanged(ctx context.Context, 1200 online bool) error { 1201 k.log.CDebugf(ctx, "Sending notification for onlineStatus: online=%v", online) 1202 return k.kbfsClient.FSOnlineStatusChangedEvent(ctx, online) 1203 } 1204 1205 // Notify implements the KeybaseService interface for KeybaseServiceBase. 1206 func (k *KeybaseServiceBase) Notify(ctx context.Context, notification *keybase1.FSNotification) error { 1207 return k.kbfsClient.FSEvent(ctx, *notification) 1208 } 1209 1210 // NotifyPathUpdated implements the KeybaseService interface for 1211 // KeybaseServiceBase. 1212 func (k *KeybaseServiceBase) NotifyPathUpdated( 1213 ctx context.Context, path string) error { 1214 return k.kbfsClient.FSPathUpdate(ctx, path) 1215 } 1216 1217 // NotifySyncStatus implements the KeybaseService interface for 1218 // KeybaseServiceBase. 1219 func (k *KeybaseServiceBase) NotifySyncStatus(ctx context.Context, 1220 status *keybase1.FSPathSyncStatus) error { 1221 return k.kbfsClient.FSSyncEvent(ctx, *status) 1222 } 1223 1224 // NotifyOverallSyncStatus implements the KeybaseService interface for 1225 // KeybaseServiceBase. 1226 func (k *KeybaseServiceBase) NotifyOverallSyncStatus( 1227 ctx context.Context, status keybase1.FolderSyncStatus) error { 1228 return k.kbfsClient.FSOverallSyncEvent(ctx, status) 1229 } 1230 1231 // NotifyFavoritesChanged implements the KeybaseService interface for 1232 // KeybaseServiceBase. 1233 func (k *KeybaseServiceBase) NotifyFavoritesChanged(ctx context.Context) error { 1234 return k.kbfsClient.FSFavoritesChangedEvent(ctx) 1235 } 1236 1237 // OnPathChange implements the SubscriptionNotifier interface. 1238 func (k *KeybaseServiceBase) OnPathChange( 1239 clientID SubscriptionManagerClientID, 1240 subscriptionIDs []SubscriptionID, path string, 1241 topics []keybase1.PathSubscriptionTopic) { 1242 subscriptionIDStrings := make([]string, 0, len(subscriptionIDs)) 1243 for _, sid := range subscriptionIDs { 1244 subscriptionIDStrings = append(subscriptionIDStrings, string(sid)) 1245 } 1246 err := k.kbfsClient.FSSubscriptionNotifyPathEvent( 1247 context.Background(), keybase1.FSSubscriptionNotifyPathEventArg{ 1248 ClientID: string(clientID), 1249 SubscriptionIDs: subscriptionIDStrings, 1250 Path: path, 1251 Topics: topics, 1252 }) 1253 if err != nil { 1254 k.log.CDebugf( 1255 context.TODO(), "Couldn't send path change notification: %+v", err) 1256 } 1257 } 1258 1259 // OnNonPathChange implements the SubscriptionNotifier interface. 1260 func (k *KeybaseServiceBase) OnNonPathChange( 1261 clientID SubscriptionManagerClientID, 1262 subscriptionIDs []SubscriptionID, topic keybase1.SubscriptionTopic) { 1263 subscriptionIDStrings := make([]string, 0, len(subscriptionIDs)) 1264 for _, sid := range subscriptionIDs { 1265 subscriptionIDStrings = append(subscriptionIDStrings, string(sid)) 1266 } 1267 err := k.kbfsClient.FSSubscriptionNotifyEvent(context.Background(), 1268 keybase1.FSSubscriptionNotifyEventArg{ 1269 ClientID: string(clientID), 1270 SubscriptionIDs: subscriptionIDStrings, 1271 Topic: topic, 1272 }) 1273 if err != nil { 1274 k.log.CDebugf( 1275 context.TODO(), 1276 "Couldn't send non-path change notification: %+v", err) 1277 } 1278 } 1279 1280 // FlushUserFromLocalCache implements the KeybaseService interface for 1281 // KeybaseServiceBase. 1282 func (k *KeybaseServiceBase) FlushUserFromLocalCache(ctx context.Context, 1283 uid keybase1.UID) { 1284 k.log.CDebugf(ctx, "Flushing cache for user %s", uid) 1285 k.setCachedUserInfo(uid, idutil.UserInfo{}) 1286 } 1287 1288 // CtxKeybaseServiceTagKey is the type used for unique context tags 1289 // used while servicing incoming keybase requests. 1290 type CtxKeybaseServiceTagKey int 1291 1292 const ( 1293 // CtxKeybaseServiceIDKey is the type of the tag for unique 1294 // operation IDs used while servicing incoming keybase requests. 1295 CtxKeybaseServiceIDKey CtxKeybaseServiceTagKey = iota 1296 ) 1297 1298 // CtxKeybaseServiceOpID is the display name for the unique operation 1299 // enqueued rekey ID tag. 1300 const CtxKeybaseServiceOpID = "KSID" 1301 1302 // FSEditListRequest implements keybase1.NotifyFSRequestInterface for 1303 // KeybaseServiceBase. 1304 func (k *KeybaseServiceBase) FSEditListRequest(ctx context.Context, 1305 req keybase1.FSEditListRequest) (err error) { 1306 ctx = CtxWithRandomIDReplayable(ctx, CtxKeybaseServiceIDKey, CtxKeybaseServiceOpID, 1307 k.log) 1308 k.log.CDebugf(ctx, "Edit list request for %s (public: %t)", 1309 req.Folder.Name, !req.Folder.Private) 1310 tlfHandle, err := getHandleFromFolderName( 1311 ctx, k.config.KBPKI(), k.config.MDOps(), k.config, req.Folder.Name, 1312 !req.Folder.Private) 1313 if err != nil { 1314 return err 1315 } 1316 1317 rootNode, _, err := k.config.KBFSOps(). 1318 GetOrCreateRootNode(ctx, tlfHandle, data.MasterBranch) 1319 if err != nil { 1320 return err 1321 } 1322 history, err := k.config.KBFSOps().GetEditHistory(ctx, 1323 rootNode.GetFolderBranch()) 1324 if err != nil { 1325 return err 1326 } 1327 1328 // TODO(KBFS-2996) Convert the edits to an RPC response. 1329 resp := keybase1.FSEditListArg{ 1330 RequestID: req.RequestID, 1331 Edits: history, 1332 } 1333 1334 k.log.CDebugf(ctx, "Sending edit history response with %d writer clusters", 1335 len(resp.Edits.History)) 1336 return k.kbfsClient.FSEditList(ctx, resp) 1337 } 1338 1339 // FSSyncStatusRequest implements keybase1.NotifyFSRequestInterface for 1340 // KeybaseServiceBase. 1341 func (k *KeybaseServiceBase) FSSyncStatusRequest(ctx context.Context, 1342 req keybase1.FSSyncStatusRequest) (err error) { 1343 k.log.CDebugf(ctx, "Got sync status request: %v", req) 1344 1345 resp := keybase1.FSSyncStatusArg{RequestID: req.RequestID} 1346 1347 // For now, just return the number of syncing bytes. 1348 jManager, err := GetJournalManager(k.config) 1349 if err == nil { 1350 status, _ := jManager.Status(ctx) 1351 resp.Status.TotalSyncingBytes = status.UnflushedBytes 1352 k.log.CDebugf(ctx, "Sending sync status response with %d syncing bytes", 1353 status.UnflushedBytes) 1354 } else { 1355 k.log.CDebugf(ctx, "No journal server, sending empty response") 1356 } 1357 1358 return k.kbfsClient.FSSyncStatus(ctx, resp) 1359 } 1360 1361 // TeamChangedByID implements keybase1.NotifyTeamInterface for 1362 // KeybaseServiceBase. 1363 func (k *KeybaseServiceBase) TeamChangedByID(ctx context.Context, 1364 arg keybase1.TeamChangedByIDArg) error { 1365 k.log.CDebugf(ctx, "Flushing cache for team %s "+ 1366 "(membershipChange=%t, keyRotated=%t, renamed=%t)", 1367 arg.TeamID, arg.Changes.MembershipChanged, 1368 arg.Changes.KeyRotated, arg.Changes.Renamed) 1369 k.setCachedTeamInfo(arg.TeamID, idutil.TeamInfo{}) 1370 1371 if arg.Changes.Renamed { 1372 k.config.KBFSOps().TeamNameChanged(ctx, arg.TeamID) 1373 } 1374 return nil 1375 } 1376 1377 // TeamChangedByName implements keybase1.NotifyTeamInterface for 1378 // KeybaseServiceBase. 1379 func (k *KeybaseServiceBase) TeamChangedByName(ctx context.Context, 1380 arg keybase1.TeamChangedByNameArg) error { 1381 // ignore 1382 return nil 1383 } 1384 1385 // TeamDeleted implements keybase1.NotifyTeamInterface for 1386 // KeybaseServiceBase. 1387 func (k *KeybaseServiceBase) TeamDeleted(ctx context.Context, 1388 teamID keybase1.TeamID) error { 1389 return nil 1390 } 1391 1392 // TeamExit implements keybase1.NotifyTeamInterface for KeybaseServiceBase. 1393 func (k *KeybaseDaemonRPC) TeamExit(context.Context, keybase1.TeamID) error { 1394 return nil 1395 } 1396 1397 // TeamRoleMapChanged implements keybase1.NotifyTeamInterface for KeybaseServiceBase. 1398 func (k *KeybaseDaemonRPC) TeamRoleMapChanged(context.Context, keybase1.UserTeamVersion) error { 1399 return nil 1400 } 1401 1402 // NewlyAddedToTeam implements keybase1.NotifyTeamInterface for 1403 // KeybaseServiceBase. 1404 func (k *KeybaseDaemonRPC) NewlyAddedToTeam(context.Context, keybase1.TeamID) error { 1405 return nil 1406 } 1407 1408 // TeamMetadataUpdate implements keybase1.NotifyTeamInterface for 1409 // KeybaseServiceBase. 1410 func (k *KeybaseDaemonRPC) TeamMetadataUpdate(context.Context) error { 1411 return nil 1412 } 1413 1414 // TeamAbandoned implements keybase1.NotifyTeamInterface for KeybaseServiceBase. 1415 func (k *KeybaseDaemonRPC) TeamAbandoned( 1416 ctx context.Context, tid keybase1.TeamID) error { 1417 k.log.CDebugf(ctx, "Implicit team %s abandoned", tid) 1418 k.setCachedTeamInfo(tid, idutil.TeamInfo{}) 1419 k.config.KBFSOps().TeamAbandoned(ctx, tid) 1420 return nil 1421 } 1422 1423 // AvatarUpdated implements keybase1.NotifyTeamInterface for KeybaseServiceBase. 1424 func (k *KeybaseDaemonRPC) AvatarUpdated(ctx context.Context, 1425 arg keybase1.AvatarUpdatedArg) error { 1426 return nil 1427 } 1428 1429 // TeamTreeMembershipsPartial implements keybase1.NotifyTeamInterface for KeybaseServiceBase. 1430 func (k *KeybaseDaemonRPC) TeamTreeMembershipsPartial(context.Context, 1431 keybase1.TeamTreeMembership) error { 1432 return nil 1433 } 1434 1435 // TeamTreeMembershipsDone implements keybase1.NotifyTeamInterface for KeybaseServiceBase. 1436 func (k *KeybaseDaemonRPC) TeamTreeMembershipsDone(context.Context, 1437 keybase1.TeamTreeMembershipsDoneResult) error { 1438 return nil 1439 } 1440 1441 // StartMigration implements keybase1.ImplicitTeamMigrationInterface for 1442 // KeybaseServiceBase. 1443 func (k *KeybaseServiceBase) StartMigration(ctx context.Context, 1444 folder keybase1.Folder) (err error) { 1445 mdServer := k.config.MDServer() 1446 if mdServer == nil { 1447 return errors.New("no mdserver") 1448 } 1449 // Making a favorite here to reuse the code that converts from 1450 // `keybase1.FolderType` into `tlf.Type`. 1451 fav := favorites.NewFolderFromProtocol(folder) 1452 handle, err := GetHandleFromFolderNameAndType( 1453 ctx, k.config.KBPKI(), k.config.MDOps(), k.config, fav.Name, fav.Type) 1454 if err != nil { 1455 return err 1456 } 1457 // Before taking the lock, first make sure this device can handle 1458 // the migration. 1459 tlfID := handle.TlfID() 1460 err = k.config.KBFSOps().CheckMigrationPerms(ctx, tlfID) 1461 if err != nil { 1462 k.log.CDebugf(ctx, "This device cannot migrate %s: %+v", tlfID, err) 1463 return err 1464 } 1465 return k.config.MDServer().StartImplicitTeamMigration(ctx, tlfID) 1466 } 1467 1468 // FinalizeMigration implements keybase1.ImplicitTeamMigrationInterface for 1469 // KeybaseServiceBase. 1470 func (k *KeybaseServiceBase) FinalizeMigration(ctx context.Context, 1471 folder keybase1.Folder) (err error) { 1472 fav := favorites.NewFolderFromProtocol(folder) 1473 handle, err := GetHandleFromFolderNameAndType( 1474 ctx, k.config.KBPKI(), k.config.MDOps(), k.config, fav.Name, fav.Type) 1475 if err != nil { 1476 return err 1477 } 1478 if handle.TypeForKeying() == tlf.TeamKeying { 1479 // Clear the cache for this implicit team, to ensure we get 1480 // all the latest key generations for the team info during the 1481 // migration. 1482 id := handle.FirstResolvedWriter() 1483 if id.IsTeamOrSubteam() { 1484 tid, err := id.AsTeam() 1485 if err != nil { 1486 return err 1487 } 1488 k.log.CDebugf(ctx, "Clearing team info for tid=%s, handle=%s", 1489 tid, handle.GetCanonicalPath()) 1490 k.setCachedTeamInfo(tid, idutil.TeamInfo{}) 1491 } 1492 } 1493 return k.config.KBFSOps().MigrateToImplicitTeam(ctx, handle.TlfID()) 1494 } 1495 1496 // GetTLFCryptKeys implements the TlfKeysInterface interface for 1497 // KeybaseServiceBase. 1498 func (k *KeybaseServiceBase) GetTLFCryptKeys(ctx context.Context, 1499 query keybase1.TLFQuery) (res keybase1.GetTLFCryptKeysRes, err error) { 1500 if ctx, err = tlfhandle.MakeExtendedIdentify( 1501 CtxWithRandomIDReplayable(ctx, 1502 CtxKeybaseServiceIDKey, CtxKeybaseServiceOpID, k.log), 1503 query.IdentifyBehavior, 1504 ); err != nil { 1505 return keybase1.GetTLFCryptKeysRes{}, err 1506 } 1507 1508 tlfHandle, err := getHandleFromFolderName( 1509 ctx, k.config.KBPKI(), k.config.MDOps(), k.config, query.TlfName, false) 1510 if err != nil { 1511 return res, err 1512 } 1513 1514 res.NameIDBreaks.CanonicalName = keybase1.CanonicalTlfName( 1515 tlfHandle.GetCanonicalName()) 1516 1517 keys, id, err := k.config.KBFSOps().GetTLFCryptKeys(ctx, tlfHandle) 1518 if err != nil { 1519 return res, err 1520 } 1521 res.NameIDBreaks.TlfID = keybase1.TLFID(id.String()) 1522 1523 for i, key := range keys { 1524 res.CryptKeys = append(res.CryptKeys, keybase1.CryptKey{ 1525 KeyGeneration: int(kbfsmd.FirstValidKeyGen) + i, 1526 Key: keybase1.Bytes32(key.Data()), 1527 }) 1528 } 1529 1530 if query.IdentifyBehavior.WarningInsteadOfErrorOnBrokenTracks() { 1531 res.NameIDBreaks.Breaks = tlfhandle.GetExtendedIdentify(ctx). 1532 GetTlfBreakAndClose() 1533 } 1534 1535 return res, nil 1536 } 1537 1538 // GetPublicCanonicalTLFNameAndID implements the TlfKeysInterface interface for 1539 // KeybaseServiceBase. 1540 func (k *KeybaseServiceBase) GetPublicCanonicalTLFNameAndID( 1541 ctx context.Context, query keybase1.TLFQuery) ( 1542 res keybase1.CanonicalTLFNameAndIDWithBreaks, err error) { 1543 if ctx, err = tlfhandle.MakeExtendedIdentify( 1544 CtxWithRandomIDReplayable(ctx, 1545 CtxKeybaseServiceIDKey, CtxKeybaseServiceOpID, k.log), 1546 query.IdentifyBehavior, 1547 ); err != nil { 1548 return keybase1.CanonicalTLFNameAndIDWithBreaks{}, err 1549 } 1550 1551 tlfHandle, err := getHandleFromFolderName( 1552 ctx, k.config.KBPKI(), k.config.MDOps(), k.config, query.TlfName, 1553 true /* public */) 1554 if err != nil { 1555 return res, err 1556 } 1557 1558 res.CanonicalName = keybase1.CanonicalTlfName( 1559 tlfHandle.GetCanonicalName()) 1560 1561 id, err := k.config.KBFSOps().GetTLFID(ctx, tlfHandle) 1562 if err != nil { 1563 return res, err 1564 } 1565 res.TlfID = keybase1.TLFID(id.String()) 1566 1567 if query.IdentifyBehavior.WarningInsteadOfErrorOnBrokenTracks() { 1568 res.Breaks = tlfhandle.GetExtendedIdentify(ctx).GetTlfBreakAndClose() 1569 } 1570 1571 return res, nil 1572 } 1573 1574 // EstablishMountDir asks the service for the current mount path 1575 func (k *KeybaseServiceBase) EstablishMountDir(ctx context.Context) ( 1576 string, error) { 1577 dir, err := k.kbfsMountClient.GetCurrentMountDir(ctx) 1578 if err != nil { 1579 k.log.CInfof(ctx, "GetCurrentMountDir fails - ", err) 1580 return "", err 1581 } 1582 if dir == "" { 1583 dirs, err := k.kbfsMountClient.GetAllAvailableMountDirs(ctx) 1584 if err != nil { 1585 k.log.CInfof(ctx, "GetAllAvailableMountDirs fails - ", err) 1586 return "", err 1587 } 1588 dir, err = chooseDefaultMount(ctx, dirs, k.log) 1589 if err != nil { 1590 k.log.CInfof(ctx, "chooseDefaultMount fails - ", err) 1591 return "", err 1592 } 1593 err2 := k.kbfsMountClient.SetCurrentMountDir(ctx, dir) 1594 if err2 != nil { 1595 k.log.CInfof(ctx, "SetCurrentMountDir fails - ", err2) 1596 } 1597 // Continue mounting even if we can't save the mount 1598 k.log.CDebugf(ctx, "Choosing mountdir %s from %v", dir, dirs) 1599 } 1600 return dir, err 1601 } 1602 1603 // PutGitMetadata implements the KeybaseService interface for 1604 // KeybaseServiceBase. 1605 func (k *KeybaseServiceBase) PutGitMetadata( 1606 ctx context.Context, folder keybase1.FolderHandle, repoID keybase1.RepoID, 1607 metadata keybase1.GitLocalMetadata) error { 1608 return k.gitClient.PutGitMetadata(ctx, keybase1.PutGitMetadataArg{ 1609 Folder: folder, 1610 RepoID: repoID, 1611 Metadata: metadata, 1612 }) 1613 } 1614 1615 // GetKVStoreClient implements the KeybaseService interface for 1616 // KeybaseServiceBase. 1617 func (k *KeybaseServiceBase) GetKVStoreClient() keybase1.KvstoreInterface { 1618 return k.kvstoreClient 1619 }