github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/libkb/upak_loader.go (about) 1 package libkb 2 3 import ( 4 "errors" 5 "fmt" 6 "time" 7 8 "sync" 9 10 lru "github.com/hashicorp/golang-lru" 11 "github.com/keybase/client/go/protocol/keybase1" 12 "golang.org/x/net/context" 13 "golang.org/x/sync/errgroup" 14 ) 15 16 // UPAK Loader is a loader for UserPlusKeysV2AllIncarnations. It's a thin user object that is 17 // almost as good for many purposes, but can be safely copied and serialized. 18 type UPAKLoader interface { 19 LoginAs(u keybase1.UID) (err error) 20 OnLogout() (err error) 21 ClearMemory() 22 Load(arg LoadUserArg) (ret *keybase1.UserPlusAllKeys, user *User, err error) 23 LoadV2(arg LoadUserArg) (ret *keybase1.UserPlusKeysV2AllIncarnations, user *User, err error) 24 LoadLite(arg LoadUserArg) (ret *keybase1.UPKLiteV1AllIncarnations, err error) 25 CheckKIDForUID(ctx context.Context, uid keybase1.UID, kid keybase1.KID) (found bool, revokedAt *keybase1.KeybaseTime, deleted bool, err error) 26 LoadUserPlusKeys(ctx context.Context, uid keybase1.UID, pollForKID keybase1.KID) (keybase1.UserPlusKeys, error) 27 LoadKeyV2(ctx context.Context, uid keybase1.UID, kid keybase1.KID) (*keybase1.UserPlusKeysV2, *keybase1.UserPlusKeysV2AllIncarnations, *keybase1.PublicKeyV2NaCl, error) 28 Invalidate(ctx context.Context, uid keybase1.UID) 29 LoadDeviceKey(ctx context.Context, uid keybase1.UID, deviceID keybase1.DeviceID) (upak *keybase1.UserPlusAllKeys, deviceKey *keybase1.PublicKey, revoked *keybase1.RevokedKey, err error) 30 LoadUPAKWithDeviceID(ctx context.Context, uid keybase1.UID, deviceID keybase1.DeviceID) (*keybase1.UserPlusKeysV2AllIncarnations, error) 31 LookupUsername(ctx context.Context, uid keybase1.UID) (NormalizedUsername, error) 32 LookupUsernameUPAK(ctx context.Context, uid keybase1.UID) (NormalizedUsername, error) 33 LookupUID(ctx context.Context, un NormalizedUsername) (keybase1.UID, error) 34 LookupUsernameAndDevice(ctx context.Context, uid keybase1.UID, did keybase1.DeviceID) (username NormalizedUsername, deviceName string, deviceType keybase1.DeviceTypeV2, err error) 35 PutUserToCache(ctx context.Context, user *User) error 36 LoadV2WithKID(ctx context.Context, uid keybase1.UID, kid keybase1.KID) (*keybase1.UserPlusKeysV2AllIncarnations, error) 37 CheckDeviceForUIDAndUsername(ctx context.Context, uid keybase1.UID, did keybase1.DeviceID, n NormalizedUsername, suppressNetworkErrors bool) error 38 Batcher(ctx context.Context, getArg func(int) *LoadUserArg, processResult func(int, *keybase1.UserPlusKeysV2AllIncarnations) error, window int) (err error) 39 } 40 41 // CachedUPAKLoader is a UPAKLoader implementation that can cache results both 42 // in memory and on disk. 43 type CachedUPAKLoader struct { 44 Contextified 45 sync.Mutex 46 cache *lru.Cache 47 locktab *LockTable 48 Freshness time.Duration 49 noCache bool 50 TestDeadlocker func() 51 currentUID keybase1.UID 52 } 53 54 // NewCachedUPAKLoader constructs a new CachedUPAKLoader 55 func NewCachedUPAKLoader(g *GlobalContext, f time.Duration) *CachedUPAKLoader { 56 c, err := lru.New(g.Env.GetUPAKCacheSize()) 57 if err != nil { 58 panic(fmt.Sprintf("could not create lru cache (size = %d)", g.Env.GetUPAKCacheSize())) 59 } 60 return &CachedUPAKLoader{ 61 Contextified: NewContextified(g), 62 Freshness: f, 63 cache: c, 64 noCache: false, 65 locktab: NewLockTable(), 66 } 67 } 68 69 func (u *CachedUPAKLoader) LoginAs(uid keybase1.UID) (err error) { 70 u.Lock() 71 defer u.Unlock() 72 u.currentUID = uid 73 return nil 74 } 75 76 func (u *CachedUPAKLoader) GetCurrentUID() keybase1.UID { 77 u.Lock() 78 defer u.Unlock() 79 return u.currentUID 80 } 81 82 func (u *CachedUPAKLoader) OnLogout() (err error) { 83 return u.LoginAs(keybase1.UID("")) 84 } 85 86 // NewUncachedUPAKLoader creates a UPAK loader that doesn't do any caching. 87 // It uses the implementation of CachedUPAKLoader but disables all caching. 88 func NewUncachedUPAKLoader(g *GlobalContext) UPAKLoader { 89 return &CachedUPAKLoader{ 90 Contextified: NewContextified(g), 91 Freshness: time.Duration(0), 92 noCache: true, 93 } 94 } 95 96 func culDBKeyV1(uid keybase1.UID) DbKey { 97 return DbKeyUID(DBUserPlusAllKeysV1, uid) 98 } 99 100 func culDBKeyVersioned(version int, uid keybase1.UID, stubMode StubMode) DbKey { 101 102 typ := DBUserPlusKeysVersioned 103 if stubMode == StubModeUnstubbed { 104 typ = DBUserPlusKeysVersionedUnstubbed 105 } 106 return DbKey{ 107 Typ: ObjType(typ), 108 Key: fmt.Sprintf("%d:%s", version, uid.String()), 109 } 110 } 111 112 func culDBKeyV2(uid keybase1.UID, stubMode StubMode) DbKey { 113 return culDBKeyVersioned(2, uid, stubMode) 114 } 115 116 func (u *CachedUPAKLoader) ClearMemory() { 117 if u.noCache { 118 return 119 } 120 u.purgeMemCache() 121 } 122 123 const UPK2MinorVersionCurrent = keybase1.UPK2MinorVersion_V6 124 125 func (u *CachedUPAKLoader) getCachedUPAKFromDB(ctx context.Context, uid keybase1.UID, stubMode StubMode) (ret *keybase1.UserPlusKeysV2AllIncarnations) { 126 var tmp keybase1.UserPlusKeysV2AllIncarnations 127 found, err := u.G().LocalDb.GetInto(&tmp, culDBKeyV2(uid, stubMode)) 128 129 if err != nil { 130 u.G().Log.CWarningf(ctx, "trouble accessing UserPlusKeysV2AllIncarnations cache: %s", err) 131 return nil 132 } 133 if !found { 134 u.G().VDL.CLogf(ctx, VLog0, "| missed disk cache") 135 return nil 136 } 137 if tmp.MinorVersion != UPK2MinorVersionCurrent { 138 u.G().VDL.CLogf(ctx, VLog0, "| found old minor version %d, but wanted %d; will overwrite with fresh UPAK", tmp.MinorVersion, UPK2MinorVersionCurrent) 139 return nil 140 } 141 142 u.G().VDL.CLogf(ctx, VLog0, "| hit disk cache (v%d)", tmp.MinorVersion) 143 u.putMemCache(ctx, uid, stubMode, tmp) 144 return &tmp 145 } 146 147 func (u *CachedUPAKLoader) getCachedUPAKFromDBMaybeTryBothSlots(ctx context.Context, uid keybase1.UID, stubMode StubMode) (ret *keybase1.UserPlusKeysV2AllIncarnations, finalStubMode StubMode) { 148 return pickBetterFromCache(func(stubMode StubMode) *keybase1.UserPlusKeysV2AllIncarnations { 149 return u.getCachedUPAKFromDB(ctx, uid, stubMode) 150 }, stubMode) 151 } 152 153 func (u *CachedUPAKLoader) getMemCacheMaybeTryBothSlots(ctx context.Context, uid keybase1.UID, stubMode StubMode) (ret *keybase1.UserPlusKeysV2AllIncarnations, finalStubMode StubMode) { 154 return pickBetterFromCache(func(stubMode StubMode) *keybase1.UserPlusKeysV2AllIncarnations { 155 return u.getMemCache(ctx, uid, stubMode) 156 }, stubMode) 157 } 158 159 func pickBetterFromCache(getter func(stubMode StubMode) *keybase1.UserPlusKeysV2AllIncarnations, stubMode StubMode) (ret *keybase1.UserPlusKeysV2AllIncarnations, finalStubMode StubMode) { 160 161 ret = getter(stubMode) 162 if stubMode == StubModeUnstubbed { 163 return ret, StubModeUnstubbed 164 } 165 166 stubbed := ret 167 unstubbed := getter(StubModeUnstubbed) 168 169 if unstubbed == nil { 170 return stubbed, StubModeStubbed 171 } 172 if stubbed == nil { 173 return unstubbed, StubModeUnstubbed 174 } 175 if unstubbed.IsOlderThan(*stubbed) { 176 return stubbed, StubModeStubbed 177 } 178 return unstubbed, StubModeUnstubbed 179 } 180 181 func (u *CachedUPAKLoader) getCachedUPAKTryMemThenDisk(ctx context.Context, uid keybase1.UID, stubMode StubMode, info *CachedUserLoadInfo) *keybase1.UserPlusKeysV2AllIncarnations { 182 upak, cacheStubMode := u.getMemCacheMaybeTryBothSlots(ctx, uid, stubMode) 183 184 if upak != nil { 185 // Note that below we check the minor version and then discard the cached object if it's 186 // stale. But no need in memory, since we'll never have the old version in memory. 187 u.G().VDL.CLogf(ctx, VLog0, "| hit memory cache (%s -> %s)", stubMode, cacheStubMode) 188 if info != nil { 189 info.InCache = true 190 } 191 return upak 192 } 193 194 upak, cacheStubMode = u.getCachedUPAKFromDBMaybeTryBothSlots(ctx, uid, stubMode) 195 if upak == nil { 196 u.G().VDL.CLogf(ctx, VLog0, "| missed cache") 197 return nil 198 } 199 200 u.G().VDL.CLogf(ctx, VLog0, "| hit disk cache (%s -> %s)", stubMode, cacheStubMode) 201 if info != nil { 202 info.InDiskCache = true 203 } 204 205 return upak 206 } 207 208 func (u *CachedUPAKLoader) getCachedUPAK(ctx context.Context, uid keybase1.UID, stubMode StubMode, info *CachedUserLoadInfo) (*keybase1.UserPlusKeysV2AllIncarnations, bool) { 209 210 if u.Freshness == time.Duration(0) || u.noCache { 211 u.G().VDL.CLogf(ctx, VLog0, "| cache miss since cache disabled") 212 return nil, false 213 } 214 215 upak := u.getCachedUPAKTryMemThenDisk(ctx, uid, stubMode, info) 216 217 if upak == nil { 218 return nil, true 219 } 220 221 diff := u.G().Clock().Now().Sub(keybase1.FromTime(upak.Uvv.CachedAt)) 222 fresh := (diff <= u.Freshness) 223 if fresh { 224 u.G().VDL.CLogf(ctx, VLog0, "| cache hit was fresh (cached %s ago)", diff) 225 } else { 226 u.G().VDL.CLogf(ctx, VLog0, "| cache hit was stale (by %s)", u.Freshness-diff) 227 } 228 if upak.Stale { 229 u.G().VDL.CLogf(ctx, VLog0, "| object is stale by persisted stale bit") 230 fresh = false 231 } 232 233 return upak, fresh 234 } 235 236 type CachedUserLoadInfo struct { 237 InCache bool 238 InDiskCache bool 239 TimedOut bool 240 StaleVersion bool 241 LoadedLeaf bool 242 LoadedUser bool 243 } 244 245 func (u *CachedUPAKLoader) Disable() { 246 u.Freshness = time.Duration(0) 247 } 248 249 func culDebug(u keybase1.UID) string { 250 return fmt.Sprintf("CachedUPAKLoader#Load(%s)", u) 251 } 252 253 func (u *CachedUPAKLoader) extractDeviceKey(upak keybase1.UserPlusAllKeys, deviceID keybase1.DeviceID) (deviceKey *keybase1.PublicKey, revoked *keybase1.RevokedKey, err error) { 254 for i := range upak.Base.RevokedDeviceKeys { 255 r := &upak.Base.RevokedDeviceKeys[i] 256 pk := &r.Key 257 if pk.DeviceID == deviceID { 258 deviceKey = pk 259 revoked = r 260 } 261 } 262 for i := range upak.Base.DeviceKeys { 263 pk := &upak.Base.DeviceKeys[i] 264 if pk.DeviceID == deviceID { 265 deviceKey = pk 266 revoked = nil 267 } 268 } 269 270 if deviceKey == nil { 271 dkey := fmt.Sprintf("%s:%s", upak.Base.Uid, deviceID) 272 return nil, nil, fmt.Errorf("device not found for %s", dkey) 273 } 274 275 return deviceKey, revoked, nil 276 } 277 278 func (u *CachedUPAKLoader) putUPAKToDB(ctx context.Context, uid keybase1.UID, stubMode StubMode, obj keybase1.UserPlusKeysV2AllIncarnations) (err error) { 279 err = u.G().LocalDb.PutObj(culDBKeyV2(uid, stubMode), nil, obj) 280 if err != nil { 281 u.G().Log.CWarningf(ctx, "Error in writing UPAK for %s %s: %s", uid, stubMode, err) 282 } 283 return err 284 } 285 286 func (u *CachedUPAKLoader) putUPAKToCache(ctx context.Context, obj *keybase1.UserPlusKeysV2AllIncarnations, stubMode StubMode) (err error) { 287 288 if u.noCache { 289 u.G().VDL.CLogf(ctx, VLog0, "| no cache enabled, so not putting UPAK") 290 return nil 291 } 292 293 uid := obj.Current.Uid 294 u.G().VDL.CLogf(ctx, VLog0, "| Caching UPAK for %s %s", uid, stubMode) 295 296 // At this point, we've gone to the server, and we checked that the user is fresh, so if we previously had a stale 297 // bit set for this user, we'll turn it off now. 298 if obj.Stale { 299 u.G().VDL.CLogf(ctx, VLog0, "| resetting stale bit to false for %s %s", uid, stubMode) 300 obj.Stale = false 301 } 302 303 existing := u.getMemCache(ctx, uid, stubMode) 304 305 if existing != nil && obj.IsOlderThan(*existing) { 306 u.G().VDL.CLogf(ctx, VLog0, "| CachedUpakLoader#putUPAKToCache: Refusing to overwrite with stale object") 307 return errors.New("stale object rejected") 308 } 309 u.putMemCache(ctx, uid, stubMode, *obj) 310 311 err = u.putUPAKToDB(ctx, uid, stubMode, *obj) 312 313 u.deleteV1UPAK(uid) 314 return err 315 } 316 317 func (u *CachedUPAKLoader) PutUserToCache(ctx context.Context, user *User) error { 318 lock := u.locktab.AcquireOnName(ctx, u.G(), user.GetUID().String()) 319 defer lock.Release(ctx) 320 upak, err := user.ExportToUPKV2AllIncarnations() 321 if err != nil { 322 return err 323 } 324 upak.Uvv.CachedAt = keybase1.ToTime(u.G().Clock().Now()) 325 err = u.putUPAKToCache(ctx, upak, StubModeFromUnstubbedBool(upak.Current.Unstubbed)) 326 return err 327 } 328 329 func (u *CachedUPAKLoader) LoadLite(arg LoadUserArg) (*keybase1.UPKLiteV1AllIncarnations, error) { 330 m, tbs := arg.m.WithTimeBuckets() 331 arg.m = m 332 defer tbs.Record("CachedUPAKLoader.LoadLite")() 333 g := m.G() 334 uid := arg.uid 335 336 if uid.IsNil() { 337 return nil, errors.New("need a UID to load a UPAK lite") 338 } 339 lock := u.locktab.AcquireOnName(m.ctx, g, uid.String()) 340 defer func() { 341 lock.Release(m.ctx) 342 }() 343 344 upakLite, err := LoadUPAKLite(arg) 345 return upakLite, err 346 } 347 348 // loadWithInfo loads a user from the CachedUPAKLoader object. The 'info' 349 // object contains information about how the request was handled, but otherwise, 350 // this method behaves like (and implements) the public CachedUPAKLoader#Load 351 // method below. If `accessor` is nil, then a deep copy of the UPAK is returned. 352 // In some cases, that deep copy can be expensive, so as for users who have lots of 353 // followees. So if you provide accessor, the UPAK won't be deep-copied, but you'll 354 // be able to access it from inside the accessor with exclusion. 355 func (u *CachedUPAKLoader) loadWithInfo(arg LoadUserArg, info *CachedUserLoadInfo, accessor func(k *keybase1.UserPlusKeysV2AllIncarnations) error, shouldReturnFullUser bool) (ret *keybase1.UserPlusKeysV2AllIncarnations, user *User, err error) { 356 357 // Add a LU= tax to this context, for all subsequent debugging 358 arg = arg.EnsureCtxAndLogTag() 359 360 // Shorthands 361 m, tbs := arg.m.WithTimeBuckets() 362 g := m.G() 363 ctx := m.Ctx() 364 365 defer m.VTrace(VLog0, culDebug(arg.uid), &err)() 366 367 if arg.uid.IsNil() { 368 if len(arg.name) == 0 { 369 return nil, nil, errors.New("need a UID or username to load UPAK from loader") 370 } 371 // Modifies the load arg, setting a UID 372 arg.uid, err = u.LookupUID(ctx, NewNormalizedUsername(arg.name)) 373 if err != nil { 374 return nil, nil, err 375 } 376 } 377 378 var lock *NamedLock 379 if !arg.cachedOnly { 380 lock = u.locktab.AcquireOnName(ctx, g, arg.uid.String()) 381 } 382 383 defer func() { 384 if lock != nil { 385 lock.Release(ctx) 386 } 387 388 if !shouldReturnFullUser { 389 user = nil 390 } 391 if user != nil && err == nil { 392 // Update the full-self cacher after the lock is released, to avoid 393 // any circular locking. 394 if fs := g.GetFullSelfer(); fs != nil && arg.self { 395 _ = fs.Update(ctx, user) 396 } 397 } 398 }() 399 400 returnUPAK := func(upak *keybase1.UserPlusKeysV2AllIncarnations, needCopy bool) (*keybase1.UserPlusKeysV2AllIncarnations, *User, error) { 401 if accessor != nil { 402 err := accessor(upak) 403 if err != nil { 404 return nil, nil, err 405 } 406 return nil, user, err 407 } 408 if needCopy { 409 fin := tbs.Record("CachedUPAKLoader.DeepCopy") 410 tmp := upak.DeepCopy() 411 upak = &tmp 412 fin() 413 } 414 return upak, user, nil 415 } 416 417 var upak *keybase1.UserPlusKeysV2AllIncarnations 418 var fresh bool 419 420 if !arg.forceReload { 421 upak, fresh = u.getCachedUPAK(ctx, arg.uid, arg.stubMode, info) 422 } 423 424 // cached UPAK is fresh or allowed to be stale, and we're not forcing a poll. 425 if upak != nil && !arg.forcePoll && (fresh || arg.staleOK) { 426 return returnUPAK(upak, true) 427 } 428 429 // If we had a cached UPAK we could return, we'd have already returned it. 430 if arg.cachedOnly { 431 var message string 432 if upak == nil { 433 message = "no cached user found" 434 } else { 435 message = "cached user found, but it was stale, and cached only" 436 } 437 return nil, nil, UserNotFoundError{UID: arg.uid, Msg: message} 438 } 439 440 if upak != nil { 441 // At this point, we have a cached UPAK but we are not confident about whether we can return it. 442 // Ask the server whether it is fresh. 443 444 if arg.forcePoll { 445 g.VDL.CLogf(ctx, VLog0, "%s: force-poll required us to repoll (fresh=%v)", culDebug(arg.uid), fresh) 446 } 447 448 if info != nil { 449 info.TimedOut = true 450 } 451 452 var sigHints *SigHints 453 var leaf *MerkleUserLeaf 454 455 sigHints, leaf, err = lookupSigHintsAndMerkleLeaf(m, arg.uid, true, arg.ToMerkleOpts()) 456 if err != nil { 457 if arg.staleOK { 458 g.VDL.CLogf(ctx, VLog0, "Got error %+v when checking for staleness; using cached UPAK", err) 459 return returnUPAK(upak, true) 460 } 461 return nil, nil, err 462 } 463 464 if info != nil { 465 info.LoadedLeaf = true 466 } 467 468 if leaf.eldest == "" { 469 g.VDL.CLogf(ctx, VLog0, "%s: cache-hit; but user is nuked, evicting", culDebug(arg.uid)) 470 471 // Our cached user turned out to be in reset state (without 472 // current sigchain), remove from cache, and then fall 473 // through. LoadUser shall return an error, which we will 474 // return to the caller. 475 u.removeMemCache(ctx, arg.uid, arg.stubMode) 476 477 err := u.G().LocalDb.Delete(culDBKeyV2(arg.uid, arg.stubMode)) 478 if err != nil { 479 u.G().Log.Warning("Failed to remove %s from disk cache: %s", arg.uid, err) 480 } 481 u.deleteV1UPAK(arg.uid) 482 } else if leaf.public != nil && leaf.public.Seqno == keybase1.Seqno(upak.Uvv.SigChain) { 483 g.VDL.CLogf(ctx, VLog0, "%s: cache-hit; fresh after poll", culDebug(arg.uid)) 484 485 upak.Uvv.CachedAt = keybase1.ToTime(g.Clock().Now()) 486 // This is only necessary to update the levelDB representation, 487 // since the previous line updates the in-memory cache satisfactorily. 488 if err := u.putUPAKToCache(ctx, upak, arg.stubMode); err != nil { 489 u.G().Log.CDebugf(ctx, "continuing past error in putUPAKToCache: %s", err) 490 } 491 492 return returnUPAK(upak, true) 493 } 494 495 if info != nil { 496 info.StaleVersion = true 497 } 498 arg.sigHints = sigHints 499 arg.merkleLeaf = leaf 500 } 501 502 g.VDL.CLogf(ctx, VLog0, "%s: LoadUser", culDebug(arg.uid)) 503 user, err = LoadUser(arg) 504 if info != nil { 505 info.LoadedUser = true 506 } 507 508 if user != nil { 509 // The `err` value might be non-nil above! Don't overwrite it. 510 var exportErr error 511 ret, exportErr = user.ExportToUPKV2AllIncarnations() 512 if exportErr != nil { 513 return nil, nil, exportErr 514 } 515 ret.Uvv.CachedAt = keybase1.ToTime(g.Clock().Now()) 516 } 517 518 // In some cases, it's OK to have a user object and an error. This comes up in 519 // Identify2 when identifying users who don't have a sigchain. Note that we'll never 520 // hit the cache in this case (for now...) 521 // 522 // Additionally, we might have an error, a cached UPAK, no user object. If 523 // stale is OK, we'll just return the stale data instead of the error. 524 if err != nil { 525 if upak != nil && arg.staleOK { 526 g.VDL.CLogf(ctx, VLog0, "Got error %+v when fetching UPAK, so using cached data", err) 527 return returnUPAK(upak, true) 528 } 529 return ret, user, err 530 } 531 532 if user == nil { 533 return nil, nil, UserNotFoundError{UID: arg.uid, Msg: "LoadUser failed"} 534 } 535 536 if err := u.putUPAKToCache(ctx, ret, arg.stubMode); err != nil { 537 m.Debug("continuing past error in putUPAKToCache: %s", err) 538 } 539 540 if u.TestDeadlocker != nil { 541 u.TestDeadlocker() 542 } 543 544 return returnUPAK(ret, false) 545 } 546 547 // Load a UserPlusKeysV2AllIncarnations from the local cache, falls back to 548 // LoadUser, and cache the user. Can only perform lookups by UID. Will return a 549 // non-nil UserPlusKeysV2AllIncarnations, or a non-nil error, but never both 550 // non-nil, nor never both nil. If we had to do a full LoadUser as part of the 551 // request, it's returned too. Convert to UserPlusAllKeys on the way out, for 552 // backwards compatibility. 553 func (u *CachedUPAKLoader) Load(arg LoadUserArg) (*keybase1.UserPlusAllKeys, *User, error) { 554 ret, user, err := u.loadWithInfo(arg, nil, nil, true) 555 556 // NOTE -- it's OK to return an error and a user, since certain code paths 557 // want both (see note in loadWithInfo). 558 var converted *keybase1.UserPlusAllKeys 559 if ret != nil { 560 tmp := keybase1.UPAKFromUPKV2AI(*ret) 561 converted = &tmp 562 } 563 564 return converted, user, err 565 } 566 567 // Load a UserPlusKeysV2AllIncarnations from the local cache, falls back to 568 // LoadUser, and cache the user. Can only perform lookups by UID. Will return a 569 // non-nil UserPlusKeysV2AllIncarnations, or a non-nil error, but never both 570 // non-nil, nor never both nil. If we had to do a full LoadUser as part of the 571 // request, it's returned too. 572 func (u *CachedUPAKLoader) LoadV2(arg LoadUserArg) (*keybase1.UserPlusKeysV2AllIncarnations, *User, error) { 573 m, tbs := arg.m.WithTimeBuckets() 574 arg.m = m 575 defer tbs.Record("CachedUPAKLoader.LoadV2")() 576 return u.loadWithInfo(arg, nil, nil, true) 577 } 578 579 func (u *CachedUPAKLoader) CheckKIDForUID(ctx context.Context, uid keybase1.UID, kid keybase1.KID) (found bool, revokedAt *keybase1.KeybaseTime, deleted bool, err error) { 580 581 var info CachedUserLoadInfo 582 larg := NewLoadUserByUIDArg(ctx, u.G(), uid).WithPublicKeyOptional() 583 upak, _, err := u.loadWithInfo(larg, &info, nil, false) 584 585 if err != nil { 586 return false, nil, false, err 587 } 588 found, revokedAt, deleted = CheckKID(upak, kid) 589 if found || info.LoadedLeaf || info.LoadedUser { 590 return found, revokedAt, deleted, nil 591 } 592 larg = larg.WithForceReload() 593 upak, _, err = u.loadWithInfo(larg, nil, nil, false) 594 if err != nil { 595 return false, nil, false, err 596 } 597 found, revokedAt, deleted = CheckKID(upak, kid) 598 return found, revokedAt, deleted, nil 599 } 600 601 func (u *CachedUPAKLoader) LoadUserPlusKeys(ctx context.Context, uid keybase1.UID, pollForKID keybase1.KID) (keybase1.UserPlusKeys, error) { 602 var up keybase1.UserPlusKeys 603 if uid.IsNil() { 604 return up, NoUIDError{} 605 } 606 607 arg := NewLoadUserArg(u.G()).WithUID(uid).WithPublicKeyOptional().WithNetContext(ctx) 608 forcePollValues := []bool{false, true} 609 610 for _, fp := range forcePollValues { 611 612 arg = arg.WithForcePoll(fp) 613 614 upak, _, err := u.Load(arg) 615 if err != nil { 616 return up, err 617 } 618 if upak == nil { 619 return up, fmt.Errorf("Nil user, nil error from LoadUser") 620 } 621 up = upak.Base 622 if pollForKID.IsNil() || up.FindKID(pollForKID) != nil { 623 break 624 } 625 626 } 627 return up, nil 628 } 629 630 // LoadKeyV2 looks through all incarnations for the user and returns the incarnation with the given 631 // KID, as well as the Key data associated with that KID. It picks the latest such 632 // incarnation if there are multiple. 633 func (u *CachedUPAKLoader) LoadKeyV2(ctx context.Context, uid keybase1.UID, kid keybase1.KID) (ret *keybase1.UserPlusKeysV2, 634 upak *keybase1.UserPlusKeysV2AllIncarnations, key *keybase1.PublicKeyV2NaCl, err error) { 635 ctx = WithLogTag(ctx, "LK") // Load key 636 defer u.G().CVTrace(ctx, VLog0, fmt.Sprintf("LoadKeyV2 uid:%s,kid:%s", uid, kid), &err)() 637 ctx, tbs := u.G().CTimeBuckets(ctx) 638 defer tbs.Record("CachedUPAKLoader.LoadKeyV2")() 639 if uid.IsNil() { 640 return nil, nil, nil, NoUIDError{} 641 } 642 643 argBase := NewLoadUserArg(u.G()).WithUID(uid).WithPublicKeyOptional().WithNetContext(ctx) 644 645 // Make the retry mechanism increasingly aggressive. See CORE-8851. 646 // It should be that a ForcePoll is good enough, but in some rare cases, 647 // people have cached values for previous pre-reset user incarnations that 648 // were incorrect. So clobber over that if it comes to it. 649 attempts := []LoadUserArg{ 650 argBase, 651 argBase.WithForcePoll(true), 652 argBase.WithForceReload(), 653 } 654 655 for i, arg := range attempts { 656 657 if i > 0 { 658 u.G().VDL.CLogf(ctx, VLog0, "| reloading with arg: %s", arg.String()) 659 } 660 661 upak, _, err := u.LoadV2(arg) 662 if err != nil { 663 return nil, nil, nil, err 664 } 665 if upak == nil { 666 return nil, nil, nil, fmt.Errorf("Nil user, nil error from LoadUser") 667 } 668 669 ret, key := upak.FindKID(kid) 670 if key != nil { 671 u.G().VDL.CLogf(ctx, VLog1, "- found kid in UPAK: %v", ret.Uid) 672 return ret, upak, key, nil 673 } 674 } 675 676 return nil, nil, nil, NotFoundError{Msg: "Not found: Key for user"} 677 } 678 679 func (u *CachedUPAKLoader) markStale(ctx context.Context, uid keybase1.UID, mode StubMode) (err error) { 680 u.G().VDL.CLogf(ctx, VLog0, "| CachedUPAKLoader#markStale(%s,%v)", uid, mode) 681 obj := u.getCachedUPAKFromDB(ctx, uid, mode) 682 if obj == nil { 683 u.G().VDL.CLogf(ctx, VLog0, "| markStale: cached user object not found") 684 return nil 685 } 686 obj.Stale = true 687 err = u.putUPAKToDB(ctx, uid, mode, *obj) 688 return err 689 } 690 691 func (u *CachedUPAKLoader) Invalidate(ctx context.Context, uid keybase1.UID) { 692 693 u.G().VDL.CLogf(ctx, VLog0, "| CachedUPAKLoader#Invalidate(%s)", uid) 694 695 if u.noCache { 696 return 697 } 698 699 lock := u.locktab.AcquireOnName(ctx, u.G(), uid.String()) 700 defer lock.Release(ctx) 701 702 for _, sm := range []StubMode{StubModeStubbed, StubModeUnstubbed} { 703 err := u.markStale(ctx, uid, sm) 704 if err != nil { 705 u.G().Log.CWarningf(ctx, "Failed to remove %s from disk cache: %s", uid, err) 706 } 707 } 708 709 // Do this after invalidation, since loading upaks from DB puts them into memory 710 u.removeMemCache(ctx, uid, StubModeUnstubbed) 711 u.removeMemCache(ctx, uid, StubModeStubbed) 712 713 u.deleteV1UPAK(uid) 714 } 715 716 // Load the PublicKey for a user's device from the local cache, falling back to LoadUser, and cache the user. 717 // If the user exists but the device doesn't, will force a load in case the device is very new. 718 func (u *CachedUPAKLoader) LoadDeviceKey(ctx context.Context, uid keybase1.UID, deviceID keybase1.DeviceID) (upakv1 *keybase1.UserPlusAllKeys, deviceKey *keybase1.PublicKey, revoked *keybase1.RevokedKey, err error) { 719 var info CachedUserLoadInfo 720 larg := NewLoadUserByUIDArg(ctx, u.G(), uid) 721 upakV2, _, err := u.loadWithInfo(larg, &info, nil, false) 722 if err != nil { 723 return nil, nil, nil, err 724 } 725 upakV1 := keybase1.UPAKFromUPKV2AI(*upakV2) 726 727 deviceKey, revoked, err = u.extractDeviceKey(upakV1, deviceID) 728 if err == nil { 729 // Early success, return 730 return &upakV1, deviceKey, revoked, err 731 } 732 733 // Try again with a forced load in case the device is very new. 734 larg = larg.WithForcePoll(true) 735 upakV2, _, err = u.loadWithInfo(larg, nil, nil, false) 736 if err != nil { 737 return nil, nil, nil, err 738 } 739 upakV1 = keybase1.UPAKFromUPKV2AI(*upakV2) 740 741 deviceKey, revoked, err = u.extractDeviceKey(upakV1, deviceID) 742 return &upakV1, deviceKey, revoked, err 743 } 744 745 // If the user exists but the device doesn't, will force a load in case the device is very new. 746 func (u *CachedUPAKLoader) LoadUPAKWithDeviceID(ctx context.Context, uid keybase1.UID, deviceID keybase1.DeviceID) (*keybase1.UserPlusKeysV2AllIncarnations, error) { 747 var info CachedUserLoadInfo 748 larg := NewLoadUserByUIDArg(ctx, u.G(), uid).WithPublicKeyOptional() 749 upakV2, _, err := u.loadWithInfo(larg, &info, nil, false) 750 if err != nil { 751 return nil, err 752 } 753 754 for _, device := range upakV2.Current.DeviceKeys { 755 if device.DeviceID.Eq(deviceID) { 756 // Early success, return 757 return upakV2, nil 758 } 759 } 760 761 // Try again with a forced load in case the device is very new. 762 larg = larg.WithForcePoll(true) 763 upakV2, _, err = u.loadWithInfo(larg, nil, nil, false) 764 if err != nil { 765 return nil, err 766 } 767 return upakV2, nil 768 } 769 770 // LookupUsername uses the UIDMapper to find a username for uid. 771 func (u *CachedUPAKLoader) LookupUsername(ctx context.Context, uid keybase1.UID) (NormalizedUsername, error) { 772 var empty NormalizedUsername 773 uids := []keybase1.UID{uid} 774 namePkgs, err := u.G().UIDMapper.MapUIDsToUsernamePackages(ctx, u.G(), uids, 0, 0, false) 775 if err != nil { 776 return empty, err 777 } 778 if len(namePkgs) == 0 { 779 return empty, UserNotFoundError{UID: uid, Msg: "in CachedUPAKLoader"} 780 } 781 782 if u.TestDeadlocker != nil { 783 u.TestDeadlocker() 784 } 785 786 return namePkgs[0].NormalizedUsername, nil 787 } 788 789 // LookupUsernameUPAK uses the upak loader to find a username for uid. 790 func (u *CachedUPAKLoader) LookupUsernameUPAK(ctx context.Context, uid keybase1.UID) (NormalizedUsername, error) { 791 var info CachedUserLoadInfo 792 arg := NewLoadUserByUIDArg(ctx, u.G(), uid).WithStaleOK(true).WithPublicKeyOptional() 793 var ret NormalizedUsername 794 _, _, err := u.loadWithInfo(arg, &info, func(upak *keybase1.UserPlusKeysV2AllIncarnations) error { 795 if upak == nil { 796 return UserNotFoundError{UID: uid, Msg: "in CachedUPAKLoader"} 797 } 798 ret = NewNormalizedUsername(upak.Current.Username) 799 return nil 800 }, false) 801 return ret, err 802 } 803 804 // LookupUID is a verified map of username -> UID. IT calls into the resolver, which gives un untrusted 805 // UID, but verifies with the UPAK loader that the mapping UID -> username is correct. 806 func (u *CachedUPAKLoader) LookupUID(ctx context.Context, un NormalizedUsername) (keybase1.UID, error) { 807 m := NewMetaContext(ctx, u.G()) 808 rres := u.G().Resolver.Resolve(m, un.String()) 809 if err := rres.GetError(); err != nil { 810 return keybase1.UID(""), err 811 } 812 un2, err := u.LookupUsername(ctx, rres.GetUID()) 813 if err != nil { 814 return keybase1.UID(""), err 815 } 816 if !un.Eq(un2) { 817 m.Warning("Unexpected mismatched usernames (uid=%s): %s != %s", rres.GetUID(), un.String(), un2.String()) 818 return keybase1.UID(""), NewBadUsernameError(un.String()) 819 } 820 return rres.GetUID(), nil 821 } 822 823 func (u *CachedUPAKLoader) lookupUsernameAndDeviceWithInfo(ctx context.Context, uid keybase1.UID, did keybase1.DeviceID, info *CachedUserLoadInfo) (username NormalizedUsername, deviceName string, deviceType keybase1.DeviceTypeV2, err error) { 824 arg := NewLoadUserByUIDArg(ctx, u.G(), uid) 825 826 // First iteration through, say it's OK to load a stale user. Note that the 827 // mappings of UID to Username and DeviceID to DeviceName are immutable, so this 828 // data can never be stale. However, our user might be out of date and lack the 829 // mappings, so the second time through, we request a fresh object. 830 staleOK := []bool{true, false} 831 for _, b := range staleOK { 832 arg = arg.WithStaleOK(b) 833 found := false 834 _, _, err := u.loadWithInfo(arg, info, func(upak *keybase1.UserPlusKeysV2AllIncarnations) error { 835 if upak == nil { 836 return nil 837 } 838 if pk := upak.FindDevice(did); pk != nil { 839 username = NewNormalizedUsername(upak.Current.Username) 840 deviceName = pk.DeviceDescription 841 deviceType = pk.DeviceType 842 found = true 843 } 844 return nil 845 }, false) 846 if err != nil { 847 return NormalizedUsername(""), "", keybase1.DeviceTypeV2_NONE, err 848 } 849 if found { 850 return username, deviceName, deviceType, nil 851 } 852 } 853 err = NotFoundError{fmt.Sprintf("UID/Device pair %s/%s not found", uid, did)} 854 return NormalizedUsername(""), "", keybase1.DeviceTypeV2_NONE, err 855 } 856 857 func (u *CachedUPAKLoader) CheckDeviceForUIDAndUsername(ctx context.Context, uid keybase1.UID, did keybase1.DeviceID, n NormalizedUsername, suppressNetworkErrs bool) (err error) { 858 arg := NewLoadUserByUIDArg(ctx, u.G(), uid).WithForcePoll(true).WithPublicKeyOptional().WithStaleOK(suppressNetworkErrs) 859 foundUser := false 860 foundDevice := false 861 isRevoked := false 862 var foundUsername NormalizedUsername 863 _, _, err = u.loadWithInfo(arg, nil, func(upak *keybase1.UserPlusKeysV2AllIncarnations) error { 864 if upak == nil { 865 return nil 866 } 867 foundUser = true 868 foundUsername = NewNormalizedUsername(upak.Current.Username) 869 if pk := upak.FindDevice(did); pk != nil { 870 foundDevice = true 871 if pk.Base.Revocation != nil { 872 isRevoked = true 873 } 874 } 875 return nil 876 }, false) 877 if err != nil { 878 return err 879 } 880 if !foundUser { 881 return UserNotFoundError{UID: uid} 882 } 883 if !foundDevice { 884 return DeviceNotFoundError{Where: "UPAKLoader", ID: did, Loaded: false} 885 } 886 if isRevoked { 887 return NewKeyRevokedError(did.String()) 888 } 889 if !n.IsNil() && !foundUsername.Eq(n) { 890 return LoggedInWrongUserError{ExistingName: foundUsername, AttemptedName: n} 891 } 892 return nil 893 } 894 895 func (u *CachedUPAKLoader) loadUserWithKIDAndInfo(ctx context.Context, uid keybase1.UID, kid keybase1.KID, info *CachedUserLoadInfo) (ret *keybase1.UserPlusKeysV2AllIncarnations, err error) { 896 argBase := NewLoadUserArg(u.G()).WithUID(uid).WithPublicKeyOptional().WithNetContext(ctx) 897 898 // See comment in LoadKeyV2 899 attempts := []LoadUserArg{ 900 argBase, 901 argBase.WithForcePoll(true), 902 argBase.WithForceReload(), 903 } 904 for _, arg := range attempts { 905 u.G().VDL.CLogf(ctx, VLog0, "| loadWithUserKIDAndInfo: loading with arg: %s", arg.String()) 906 ret, _, err = u.loadWithInfo(arg, info, nil, false) 907 if err == nil && ret != nil && (kid.IsNil() || ret.HasKID(kid)) { 908 u.G().VDL.CLogf(ctx, VLog0, "| loadWithUserKIDAndInfo: UID/KID %s/%s found", uid, kid) 909 return ret, nil 910 } 911 } 912 if err == nil { 913 err = NotFoundError{fmt.Sprintf("UID/KID pair %s/%s not found", uid, kid)} 914 } 915 return nil, err 916 } 917 918 func (u *CachedUPAKLoader) LoadV2WithKID(ctx context.Context, uid keybase1.UID, kid keybase1.KID) (*keybase1.UserPlusKeysV2AllIncarnations, error) { 919 return u.loadUserWithKIDAndInfo(ctx, uid, kid, nil) 920 } 921 922 func (u *CachedUPAKLoader) LookupUsernameAndDevice(ctx context.Context, uid keybase1.UID, did keybase1.DeviceID) (username NormalizedUsername, deviceName string, deviceType keybase1.DeviceTypeV2, err error) { 923 return u.lookupUsernameAndDeviceWithInfo(ctx, uid, did, nil) 924 } 925 926 // v1 UPAKs are all legacy and need to be gradually cleaned from cache. 927 func (u *CachedUPAKLoader) deleteV1UPAK(uid keybase1.UID) { 928 err := u.G().LocalDb.Delete(culDBKeyV1(uid)) 929 if err != nil { 930 u.G().Log.Warning("Failed to remove %s v1 object from disk cache: %s", uid, err) 931 } 932 } 933 934 func memCacheKey(u keybase1.UID, stubMode StubMode) string { 935 return fmt.Sprintf("%s-%s", u, stubMode) 936 } 937 938 func (u *CachedUPAKLoader) getMemCache(ctx context.Context, uid keybase1.UID, stubMode StubMode) *keybase1.UserPlusKeysV2AllIncarnations { 939 val, ok := u.cache.Get(memCacheKey(uid, stubMode)) 940 if !ok { 941 return nil 942 } 943 944 upak, ok := val.(keybase1.UserPlusKeysV2AllIncarnations) 945 if !ok { 946 u.G().Log.CWarningf(ctx, "invalid type in upak cache: %T", val) 947 return nil 948 } 949 950 return &upak 951 } 952 953 func (u *CachedUPAKLoader) putMemCache(ctx context.Context, uid keybase1.UID, stubMode StubMode, upak keybase1.UserPlusKeysV2AllIncarnations) { 954 u.cache.Add(memCacheKey(uid, stubMode), upak) 955 } 956 957 func (u *CachedUPAKLoader) removeMemCache(ctx context.Context, uid keybase1.UID, stubMode StubMode) { 958 u.cache.Remove(memCacheKey(uid, stubMode)) 959 } 960 961 func (u *CachedUPAKLoader) purgeMemCache() { 962 u.cache.Purge() 963 } 964 965 func checkDeviceValidForUID(ctx context.Context, u UPAKLoader, uid keybase1.UID, did keybase1.DeviceID) error { 966 var nnu NormalizedUsername 967 return u.CheckDeviceForUIDAndUsername(ctx, uid, did, nnu, false) 968 } 969 970 func CheckCurrentUIDDeviceID(m MetaContext) (err error) { 971 defer m.Trace("CheckCurrentUIDDeviceID", &err)() 972 uid := m.G().Env.GetUID() 973 if uid.IsNil() { 974 return NoUIDError{} 975 } 976 did := m.G().Env.GetDeviceIDForUID(uid) 977 if did.IsNil() { 978 return NoDeviceError{fmt.Sprintf("for UID %s", uid)} 979 } 980 return checkDeviceValidForUID(m.Ctx(), m.G().GetUPAKLoader(), uid, did) 981 } 982 983 // Batcher loads a batch of UPAKs with the given window width. It keeps calling getArg(i) with an 984 // increasing i, until that getArg return nil, in which case the production of UPAK loads is over. 985 // UPAKs will be loaded and fed into processResult() as they come in. Both getArg() and processResult() 986 // are called in the same mutex to simplify synchronization. 987 func (u *CachedUPAKLoader) Batcher(ctx context.Context, getArg func(int) *LoadUserArg, processResult func(int, *keybase1.UserPlusKeysV2AllIncarnations) error, window int) (err error) { 988 if window == 0 { 989 window = 10 990 } 991 992 ctx = WithLogTag(ctx, "LUB") 993 eg, ctx := errgroup.WithContext(ctx) 994 defer u.G().CTrace(ctx, "CachedUPAKLoader#Batcher", &err)() 995 996 type argWithIndex struct { 997 i int 998 arg LoadUserArg 999 } 1000 args := make(chan argWithIndex) 1001 var mut sync.Mutex 1002 1003 // Make a stream of args, and send them down the channel 1004 var stopBatch bool 1005 eg.Go(func() error { 1006 defer close(args) 1007 for i := 0; !stopBatch; i++ { 1008 mut.Lock() 1009 arg := getArg(i) 1010 mut.Unlock() 1011 if arg == nil { 1012 return nil 1013 } 1014 select { 1015 case args <- argWithIndex{i, arg.WithNetContext(ctx)}: 1016 case <-ctx.Done(): 1017 return ctx.Err() 1018 } 1019 } 1020 return nil 1021 }) 1022 1023 for i := 0; i < window; i++ { 1024 eg.Go(func() (err error) { 1025 // If we receive an error, kill the pipeline 1026 defer func() { 1027 if err != nil { 1028 mut.Lock() 1029 defer mut.Unlock() 1030 u.G().Log.CDebugf(ctx, "CachedUPAKLoader#Batcher unable to complete batch: %v, setting stopBatch=true", err) 1031 stopBatch = true 1032 } 1033 }() 1034 for awi := range args { 1035 arg := awi.arg 1036 _, _, err = u.loadWithInfo(arg, nil, func(u *keybase1.UserPlusKeysV2AllIncarnations) error { 1037 if processResult != nil { 1038 mut.Lock() 1039 err = processResult(awi.i, u) 1040 mut.Unlock() 1041 if err != nil { 1042 return err 1043 } 1044 } 1045 return nil 1046 }, false) 1047 if err != nil { 1048 return err 1049 } 1050 } 1051 return nil 1052 }) 1053 } 1054 1055 return eg.Wait() 1056 }