github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/teams/loader.go (about) 1 package teams 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "sort" 8 "sync" 9 "time" 10 11 "golang.org/x/net/context" 12 13 "github.com/keybase/client/go/gregor" 14 "github.com/keybase/client/go/libkb" 15 "github.com/keybase/client/go/protocol/keybase1" 16 hidden "github.com/keybase/client/go/teams/hidden" 17 storage "github.com/keybase/client/go/teams/storage" 18 pkgErrors "github.com/pkg/errors" 19 ) 20 21 // Show detailed team profiling 22 var teamEnv struct { 23 Profile bool 24 UserPreloadEnable bool 25 UserPreloadParallel bool 26 UserPreloadWait bool 27 ProofSetParallel bool 28 } 29 30 func init() { 31 teamEnv.Profile = os.Getenv("KEYBASE_TEAM_PROF") == "1" 32 teamEnv.UserPreloadEnable = os.Getenv("KEYBASE_TEAM_PE") == "1" 33 teamEnv.UserPreloadParallel = os.Getenv("KEYBASE_TEAM_PP") == "1" 34 teamEnv.UserPreloadWait = os.Getenv("KEYBASE_TEAM_PW") == "1" 35 teamEnv.ProofSetParallel = os.Getenv("KEYBASE_TEAM_SP") == "0" 36 } 37 38 // How long until the tail of a team sigchain is considered non-fresh 39 const freshnessLimit = time.Duration(1) * time.Hour 40 41 // Load a Team from the TeamLoader. 42 // Can be called from inside the teams package. 43 func Load(ctx context.Context, g *libkb.GlobalContext, lArg keybase1.LoadTeamArg) (*Team, error) { 44 teamData, hidden, err := g.GetTeamLoader().Load(ctx, lArg) 45 if err != nil { 46 return nil, err 47 } 48 team := NewTeam(ctx, g, teamData, hidden) 49 50 if lArg.RefreshUIDMapper { 51 // If we just loaded the group, then inform the UIDMapper of any UID->EldestSeqno 52 // mappings, so that we're guaranteed they aren't stale. 53 team.refreshUIDMapper(ctx, g) 54 } 55 56 // OK if it errors; just move on and return the team. 57 _, _ = team.calculateAndCacheMemberCount(ctx) 58 59 return team, nil 60 } 61 62 // Loader of keybase1.TeamData objects. Handles caching. 63 // Because there is one of this global object and it is attached to G, 64 // its Load interface must return a keybase1.TeamData not a teams.Team. 65 // To load a teams.Team use the package-level function Load. 66 // Threadsafe. 67 type TeamLoader struct { 68 libkb.Contextified 69 world LoaderContext 70 storage *storage.Storage 71 merkleStorage *storage.Merkle 72 // Single-flight locks per team ID. 73 // (Private and public loads of the same ID will block each other, should be fine) 74 locktab *libkb.LockTable 75 76 // Cache lookups of team name -> ID for a few seconds, to absorb bursts of lookups 77 // from the frontend 78 nameLookupBurstCache *libkb.BurstCache 79 80 // We can get pushed by the server into "force repoll" mode, in which we're 81 // not getting cache invalidations. An example: when Coyne or Nojima revokes 82 // a device. We want to cut down on notification spam. So instead, all attempts 83 // to load a team result in a preliminary poll for freshness, which this state is enabled. 84 forceRepollMutex sync.RWMutex 85 forceRepollUntil gregor.TimeOrOffset 86 } 87 88 var _ libkb.TeamLoader = (*TeamLoader)(nil) 89 90 func NewTeamLoader(g *libkb.GlobalContext, world LoaderContext, storage *storage.Storage, merkleStorage *storage.Merkle) *TeamLoader { 91 return &TeamLoader{ 92 Contextified: libkb.NewContextified(g), 93 world: world, 94 storage: storage, 95 merkleStorage: merkleStorage, 96 nameLookupBurstCache: libkb.NewBurstCache(g, 100, 10*time.Second, "SubteamNameToID"), 97 locktab: libkb.NewLockTable(), 98 } 99 } 100 101 // NewTeamLoaderAndInstall creates a new loader and installs it into G. 102 func NewTeamLoaderAndInstall(g *libkb.GlobalContext) *TeamLoader { 103 world := NewLoaderContextFromG(g) 104 st := storage.NewStorage(g) 105 mst := storage.NewMerkle() 106 l := NewTeamLoader(g, world, st, mst) 107 g.SetTeamLoader(l) 108 g.AddLogoutHook(l, "teamLoader") 109 g.AddDbNukeHook(l, "teamLoader") 110 return l 111 } 112 113 func (l *TeamLoader) Load(ctx context.Context, lArg keybase1.LoadTeamArg) (res *keybase1.TeamData, hidden *keybase1.HiddenTeamChain, err error) { 114 me, err := l.world.getMe(ctx) 115 if err != nil { 116 return nil, nil, err 117 } 118 if me.IsNil() && !lArg.Public { 119 return nil, nil, libkb.NewLoginRequiredError("login required to load a private team") 120 } 121 return l.load1(ctx, me, lArg) 122 } 123 124 func newFrozenChain(chain *keybase1.TeamSigChainState) keybase1.TeamSigChainState { 125 return keybase1.TeamSigChainState{ 126 Id: chain.Id, 127 Public: chain.Public, 128 LastSeqno: chain.LastSeqno, 129 LastLinkID: chain.LastLinkID, 130 } 131 } 132 133 func (l *TeamLoader) Freeze(ctx context.Context, teamID keybase1.TeamID) (err error) { 134 defer l.G().CTrace(ctx, fmt.Sprintf("TeamLoader#Freeze(%s)", teamID), &err)() 135 lock := l.locktab.AcquireOnName(ctx, l.G(), teamID.String()) 136 defer lock.Release(ctx) 137 mctx := libkb.NewMetaContext(ctx, l.G()) 138 td, frozen, tombstoned := l.storage.Get(mctx, teamID, teamID.IsPublic()) 139 if frozen || td == nil { 140 return nil 141 } 142 newTD := &keybase1.TeamData{ 143 Frozen: true, 144 Tombstoned: tombstoned, 145 Chain: newFrozenChain(&td.Chain), 146 } 147 l.storage.Put(mctx, newTD) 148 return nil 149 } 150 151 func (l *TeamLoader) Tombstone(ctx context.Context, teamID keybase1.TeamID) (err error) { 152 defer l.G().CTrace(ctx, fmt.Sprintf("TeamLoader#Tombstone(%s)", teamID), &err)() 153 lock := l.locktab.AcquireOnName(ctx, l.G(), teamID.String()) 154 defer lock.Release(ctx) 155 mctx := libkb.NewMetaContext(ctx, l.G()) 156 td, frozen, tombstoned := l.storage.Get(mctx, teamID, teamID.IsPublic()) 157 if tombstoned || td == nil { 158 return nil 159 } 160 newTD := &keybase1.TeamData{ 161 Frozen: frozen, 162 Tombstoned: true, 163 Chain: newFrozenChain(&td.Chain), 164 } 165 l.storage.Put(mctx, newTD) 166 return nil 167 } 168 169 func (l *TeamLoader) HintLatestSeqno(ctx context.Context, teamID keybase1.TeamID, seqno keybase1.Seqno) error { 170 // Single-flight lock by team ID. 171 lock := l.locktab.AcquireOnName(ctx, l.G(), teamID.String()) 172 defer lock.Release(ctx) 173 mctx := libkb.NewMetaContext(ctx, l.G()) 174 175 // Load from the cache 176 td, frozen, tombstoned := l.storage.Get(mctx, teamID, teamID.IsPublic()) 177 if frozen || tombstoned || td == nil { 178 // Nothing to store the hint on. 179 return nil 180 } 181 182 if seqno < td.LatestSeqnoHint { 183 // The hint is behind the times, ignore. 184 return nil 185 } 186 187 td.LatestSeqnoHint = seqno 188 l.storage.Put(mctx, td) 189 return nil 190 } 191 192 // Get whether a team is open. Returns an arbitrarily stale answer. 193 func (l *TeamLoader) IsOpenCached(ctx context.Context, teamID keybase1.TeamID) (bool, error) { 194 // Single-flight lock by team ID. 195 lock := l.locktab.AcquireOnName(ctx, l.G(), teamID.String()) 196 defer lock.Release(ctx) 197 mctx := libkb.NewMetaContext(ctx, l.G()) 198 199 // Load from the cache 200 td, frozen, tombstoned := l.storage.Get(mctx, teamID, teamID.IsPublic()) 201 if frozen || tombstoned || td == nil { 202 return false, fmt.Errorf("team not available data:%v frozen:%v tombstoned:%v", td != nil, frozen, tombstoned) 203 } 204 return td.Chain.Open, nil 205 } 206 207 type nameLookupBurstCacheKey struct { 208 teamName keybase1.TeamName 209 public bool 210 } 211 212 func (n nameLookupBurstCacheKey) String() string { 213 return fmt.Sprintf("%s:%v", n.teamName.String(), n.public) 214 } 215 216 // Resolve a team name to a team ID. 217 // Will always hit the server for subteams. The server can lie in this return value. 218 func (l *TeamLoader) ResolveNameToIDUntrusted(ctx context.Context, teamName keybase1.TeamName, public bool, allowCache bool) (id keybase1.TeamID, err error) { 219 220 defer l.G().CVTrace(ctx, libkb.VLog0, fmt.Sprintf("resolveNameToUIDUntrusted(%s,%v,%v)", teamName.String(), public, allowCache), &err)() 221 222 // For root team names, just hash. 223 if teamName.IsRootTeam() { 224 return teamName.ToTeamID(public), nil 225 } 226 227 if !allowCache { 228 return resolveNameToIDUntrustedAPICall(ctx, l.G(), teamName, public) 229 } 230 231 var idVoidPointer interface{} 232 key := nameLookupBurstCacheKey{teamName, public} 233 idVoidPointer, err = l.nameLookupBurstCache.Load(ctx, key, l.makeNameLookupBurstCacheLoader(ctx, l.G(), key)) 234 if err != nil { 235 return keybase1.TeamID(""), err 236 } 237 if idPointer, ok := idVoidPointer.(*keybase1.TeamID); ok && idPointer != nil { 238 id = *idPointer 239 } else { 240 return keybase1.TeamID(""), errors.New("bad cast out of nameLookupBurstCache") 241 } 242 return id, nil 243 } 244 245 func resolveNameToIDUntrustedAPICall(ctx context.Context, g *libkb.GlobalContext, teamName keybase1.TeamName, public bool) (id keybase1.TeamID, err error) { 246 mctx := libkb.NewMetaContext(ctx, g) 247 arg := libkb.NewAPIArg("team/get") 248 arg.SessionType = libkb.APISessionTypeREQUIRED 249 arg.Args = libkb.HTTPArgs{ 250 "name": libkb.S{Val: teamName.String()}, 251 "lookup_only": libkb.B{Val: true}, 252 "public": libkb.B{Val: public}, 253 } 254 255 var rt rawTeam 256 if err := mctx.G().API.GetDecode(mctx, arg, &rt); err != nil { 257 return id, err 258 } 259 id = rt.ID 260 if !id.Exists() { 261 return id, fmt.Errorf("could not resolve team name: %v", teamName.String()) 262 } 263 return id, nil 264 } 265 266 func (l *TeamLoader) makeNameLookupBurstCacheLoader(ctx context.Context, g *libkb.GlobalContext, key nameLookupBurstCacheKey) libkb.BurstCacheLoader { 267 return func() (obj interface{}, err error) { 268 id, err := resolveNameToIDUntrustedAPICall(ctx, g, key.teamName, key.public) 269 if err != nil { 270 return nil, err 271 } 272 return &id, nil 273 } 274 } 275 276 // Load1 unpacks the loadArg, calls load2, and does some final checks. 277 // The key difference between load1 and load2 is that load2 is recursive (for subteams). 278 func (l *TeamLoader) load1(ctx context.Context, me keybase1.UserVersion, lArg keybase1.LoadTeamArg) (data *keybase1.TeamData, hiddenChain *keybase1.HiddenTeamChain, err error) { 279 mctx := libkb.NewMetaContext(ctx, l.G()) 280 err = l.checkArg(ctx, lArg) 281 if err != nil { 282 return nil, nil, err 283 } 284 285 var teamName *keybase1.TeamName 286 if len(lArg.Name) > 0 { 287 teamNameParsed, err := keybase1.TeamNameFromString(lArg.Name) 288 if err != nil { 289 return nil, nil, fmt.Errorf("invalid team name: %v", err) 290 } 291 teamName = &teamNameParsed 292 } 293 294 teamID := lArg.ID 295 // Resolve the name to team ID. Will always hit the server for subteams. 296 // It is safe for the answer to be wrong because the name is checked on the way out, 297 // and the merkle tree check guarantees one sigchain per team id. 298 if !teamID.Exists() { 299 teamID, err = l.ResolveNameToIDUntrusted(ctx, *teamName, lArg.Public, lArg.AllowNameLookupBurstCache) 300 if err != nil { 301 mctx.Debug("TeamLoader looking up team by name failed: %v -> %v", *teamName, err) 302 if code, ok := libkb.GetAppStatusCode(err); ok && code == keybase1.StatusCode_SCTeamNotFound { 303 mctx.Debug("replacing error: %v", err) 304 return nil, nil, NewTeamDoesNotExistError(lArg.Public, teamName.String()) 305 } 306 return nil, nil, err 307 } 308 } 309 310 defer func() { 311 if hidden.ShouldClearSupportFlagOnError(err) { 312 mctx.Debug("Clearing support hidden chain flag for team %s because of error %v in team loader (load1)", teamID, err) 313 mctx.G().GetHiddenTeamChainManager().ClearSupportFlagIfFalse(mctx, teamID) 314 } 315 }() 316 317 mungedForceRepoll := lArg.ForceRepoll 318 mungedWantMembers, err := l.mungeWantMembers(ctx, lArg.Refreshers.WantMembers) 319 if err != nil { 320 mctx.Debug("TeamLoader munge failed: %v", err) 321 // drop the error and just force a repoll. 322 mungedForceRepoll = true 323 mungedWantMembers = nil 324 } 325 326 ret, err := l.load2(ctx, load2ArgT{ 327 teamID: teamID, 328 329 needAdmin: lArg.NeedAdmin, 330 needKeyGeneration: lArg.Refreshers.NeedKeyGeneration, 331 needApplicationsAtGenerations: lArg.Refreshers.NeedApplicationsAtGenerations, 332 needApplicationsAtGenerationsWithKBFS: lArg.Refreshers.NeedApplicationsAtGenerationsWithKBFS, 333 needKBFSKeyGeneration: lArg.Refreshers.NeedKBFSKeyGeneration, 334 wantMembers: mungedWantMembers, 335 wantMembersRole: lArg.Refreshers.WantMembersRole, 336 forceFullReload: lArg.ForceFullReload, 337 forceRepoll: mungedForceRepoll, 338 staleOK: lArg.StaleOK, 339 public: lArg.Public, 340 auditMode: lArg.AuditMode, 341 skipNeedHiddenRotateCheck: lArg.SkipNeedHiddenRotateCheck, 342 343 needSeqnos: nil, 344 readSubteamID: nil, 345 346 me: me, 347 }) 348 switch err := err.(type) { 349 case TeamDoesNotExistError: 350 if teamName == nil { 351 return nil, nil, err 352 } 353 // Replace the not found error so that it has a name instead of team ID. 354 // If subteams are involved the name might not correspond to the ID 355 // but it's better to have this understandable error message that's accurate 356 // most of the time than one with an ID that's always accurate. 357 mctx.Debug("replacing error: %v", err) 358 return nil, nil, NewTeamDoesNotExistError(lArg.Public, teamName.String()) 359 case nil: 360 default: 361 return nil, nil, err 362 } 363 if ret == nil { 364 return nil, nil, fmt.Errorf("team loader fault: got nil from load2") 365 } 366 367 // Public teams are allowed to be behind on secrets since you can load a 368 // public team you're not in. Restricted bot members don't have any secrets 369 // and are also exempt. 370 if !l.hasSyncedSecrets(mctx, ret.teamShim()) && 371 !(ret.team.Chain.Public || ret.team.Chain.UserRole(me).IsRestrictedBot()) { 372 // this should not happen 373 return nil, nil, fmt.Errorf("missing secrets for team") 374 } 375 376 // Check team name on the way out 377 // The snapshot may have already been written to cache, but that should be ok, 378 // because the cache is keyed by ID. 379 if teamName != nil { 380 // (TODO: this won't work for renamed level 3 teams or above. There's work on this in miles/teamloader-names) 381 if !teamName.Eq(ret.team.Name) { 382 return nil, nil, fmt.Errorf("team name mismatch: %v != %v", ret.team.Name, teamName.String()) 383 } 384 } 385 386 if ShouldRunBoxAudit(mctx) { 387 newMctx, shouldReload := VerifyBoxAudit(mctx, teamID) 388 if shouldReload { 389 return l.load1(newMctx.Ctx(), me, lArg) 390 } 391 } else { 392 mctx.Debug("Box auditor feature flagged off; not checking jail during team load...") 393 } 394 395 return &ret.team, ret.hidden, nil 396 } 397 398 func (l *TeamLoader) checkArg(ctx context.Context, lArg keybase1.LoadTeamArg) error { 399 hasID := lArg.ID.Exists() 400 hasName := len(lArg.Name) > 0 401 if hasID { 402 id, err := keybase1.TeamIDFromString(lArg.ID.String()) 403 if err != nil { 404 return fmt.Errorf("team load arg has invalid ID: %q", lArg.ID) 405 } 406 if id.IsPublic() != lArg.Public { 407 return libkb.NewTeamVisibilityError(lArg.Public, id.IsPublic()) 408 } 409 } 410 if !hasID && !hasName { 411 return fmt.Errorf("team load arg must have either ID or Name") 412 } 413 return nil 414 } 415 416 // Mostly the same as the public keybase.LoadTeamArg 417 // but only supports loading by ID, and has neededSeqnos. 418 type load2ArgT struct { 419 teamID keybase1.TeamID 420 421 reason string // optional tag for debugging why this load is happening 422 423 needAdmin bool 424 needKeyGeneration keybase1.PerTeamKeyGeneration 425 needApplicationsAtGenerations map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication 426 needApplicationsAtGenerationsWithKBFS map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication 427 needKBFSKeyGeneration keybase1.TeamKBFSKeyRefresher 428 // wantMembers here is different from wantMembers on LoadTeamArg: 429 // The EldestSeqno's should not be 0. 430 wantMembers []keybase1.UserVersion 431 wantMembersRole keybase1.TeamRole 432 forceFullReload bool 433 forceRepoll bool 434 staleOK bool 435 public bool 436 skipNeedHiddenRotateCheck bool 437 skipSeedCheck bool 438 foundRKMHole bool // if the previous load found an RKM hole, this is set 439 440 auditMode keybase1.AuditMode 441 442 needSeqnos []keybase1.Seqno 443 // Non-nil if we are loading an ancestor for the greater purpose of 444 // loading a subteam. This parameter helps the server figure out whether 445 // to give us a subteam-reader version of the team. 446 // If and only if this is set, load2 is allowed to return a secret-less TeamData. 447 // Load1 can return secret-less TeamData if the team is public or the 448 // current user is a restricted bot member. 449 readSubteamID *keybase1.TeamID 450 451 // If the user is logged out, this will be a nil UserVersion, meaning 452 /// me.IsNil() will be true. 453 me keybase1.UserVersion 454 } 455 456 type load2ResT struct { 457 team keybase1.TeamData 458 hidden *keybase1.HiddenTeamChain 459 didRepoll bool 460 } 461 462 func (l load2ResT) teamShim() *TeamShim { 463 return &TeamShim{Data: &l.team, Hidden: l.hidden} 464 } 465 466 // Load2 does the rest of the work loading a team. 467 // It is `playchain` described in the pseudocode in teamplayer.txt 468 func (l *TeamLoader) load2(ctx context.Context, arg load2ArgT) (ret *load2ResT, err error) { 469 ctx = libkb.WithLogTag(ctx, "LT") // Load team 470 if arg.reason != "" { 471 ctx = libkb.WithLogTag(ctx, "LT2") // Load team recursive 472 } 473 mctx := libkb.NewMetaContext(ctx, l.G()) 474 475 traceLabel := fmt.Sprintf("TeamLoader#load2(%v, public:%v)", arg.teamID, arg.public) 476 if len(arg.reason) > 0 { 477 traceLabel = traceLabel + " '" + arg.reason + "'" 478 } 479 480 defer l.G().CTrace(ctx, traceLabel, &err)() 481 ret, err = l.load2Inner(ctx, arg) 482 if hidden.ShouldClearSupportFlagOnError(err) { 483 mctx.Debug("Clearing support hidden chain flag for team %s because of error %v in team loader (load2)", arg.teamID, err) 484 mctx.G().GetHiddenTeamChainManager().ClearSupportFlagIfFalse(mctx, arg.teamID) 485 } 486 return ret, err 487 } 488 489 func (l *TeamLoader) load2Inner(ctx context.Context, arg load2ArgT) (*load2ResT, error) { 490 491 // Single-flight lock by team ID. 492 lock := l.locktab.AcquireOnName(ctx, l.G(), arg.teamID.String()) 493 defer lock.Release(ctx) 494 495 return l.load2InnerLocked(ctx, arg) 496 } 497 498 func (l *TeamLoader) load2InnerLocked(ctx context.Context, arg load2ArgT) (res *load2ResT, err error) { 499 const nRetries = 3 500 for i := 0; i < nRetries; i++ { 501 res, err = l.load2InnerLockedRetry(ctx, arg) 502 switch pkgErrors.Cause(err).(type) { 503 case nil: 504 return res, nil 505 case MissingReaderKeyMaskError: 506 l.G().Log.CDebugf(ctx, "Got MissingReaderKeyMaskError (%s); retrying with forceFullReload=true", err.Error()) 507 arg.foundRKMHole = true 508 origErr := err 509 res, err = l.load2InnerLockedRetry(ctx, arg) 510 if err == nil { 511 l.G().Log.CDebugf(ctx, "Found an holes in RKMS in which busting the cache saved the day (original error was: %s)", origErr.Error()) 512 } 513 return res, err 514 case ProofError: 515 if arg.forceRepoll { 516 return res, err 517 } 518 // Something went wrong, throw out the cache and try again. 519 l.G().Log.CDebugf(ctx, "Got proof error (%s); trying again with forceRepoll=true", err.Error()) 520 arg.forceRepoll = true 521 arg.forceFullReload = true 522 origErr := err 523 res, err = l.load2InnerLockedRetry(ctx, arg) 524 if err == nil { 525 l.G().Log.CDebugf(ctx, "Found an unexpected TeamLoader case in which busting the cache saved the day (original error was: %s)", origErr.Error()) 526 } 527 return res, err 528 case GreenLinkError: 529 // Try again 530 l.G().Log.CDebugf(ctx, "TeamLoader retrying after green link") 531 arg.forceRepoll = true 532 continue 533 } 534 return res, err 535 } 536 if err == nil { 537 // Should never happen 538 return res, fmt.Errorf("failed retryable team load") 539 } 540 // Return the last error 541 return res, err 542 } 543 544 func (l *TeamLoader) checkHiddenResponse(mctx libkb.MetaContext, hiddenPackage *hidden.LoaderPackage, hiddenResp *libkb.MerkleHiddenResponse) (hiddenIsFresh bool, err error) { 545 mctx.Debug("hiddenResp: %+v UncommittedSeqno %+v", hiddenResp, hiddenResp.UncommittedSeqno) 546 547 switch hiddenResp.RespType { 548 case libkb.MerkleHiddenResponseTypeNONE: 549 mctx.Debug("Skipping CheckHiddenMerklePathResponseAndAddRatchets as no hidden data was received. If the server had to show us the hidden chain and didn't, we will error out later (once we can establish our role in the team).") 550 return true, nil 551 case libkb.MerkleHiddenResponseTypeFLAGOFF: 552 mctx.Debug("Skipping CheckHiddenMerklePathResponseAndAddRatchets as the hidden flag is off.") 553 return true, nil 554 default: 555 return hiddenPackage.CheckHiddenMerklePathResponseAndAddRatchets(mctx, hiddenResp) 556 } 557 } 558 559 func (l *TeamLoader) load2InnerLockedRetry(ctx context.Context, arg load2ArgT) (*load2ResT, error) { 560 ctx, tbs := l.G().CTimeBuckets(ctx) 561 mctx := libkb.NewMetaContext(ctx, l.G()) 562 tracer := l.G().CTimeTracer(ctx, "TeamLoader.load2ILR", teamEnv.Profile) 563 defer tracer.Finish() 564 565 defer tbs.LogIfNonZero(ctx, "API.request") 566 567 var err error 568 var didRepoll bool 569 lkc := newLoadKeyCache() 570 571 // Fetch from cache 572 tracer.Stage("cache load") 573 tailCheckRet, frozen, tombstoned := l.storage.Get(mctx, arg.teamID, arg.public) 574 if tombstoned { 575 return nil, NewTeamTombstonedError() 576 } 577 578 // Fetch last polled time from merkle cache 579 merklePolledAt := l.merkleStorage.Get(mctx, arg.teamID, arg.public) 580 581 var ret *keybase1.TeamData 582 if !frozen && !arg.forceFullReload { 583 // Load from cache 584 ret = tailCheckRet 585 } 586 587 if ret != nil && !ret.Chain.Reader.Eq(arg.me) { 588 // Check that we are the same person as when this team was last loaded as a courtesy. 589 // This should never happen. We shouldn't be able to decrypt someone else's snapshot. 590 mctx.Warning("TeamLoader discarding snapshot for wrong user: (%v, %v) != (%v, %v)", 591 arg.me.Uid, arg.me.EldestSeqno, ret.Chain.Reader.Uid, ret.Chain.Reader.EldestSeqno) 592 ret = nil 593 } 594 595 var cachedName *keybase1.TeamName 596 if ret != nil && !ret.Name.IsNil() { 597 cachedName = &ret.Name 598 } 599 600 hiddenPackage, err := l.hiddenPackage(mctx, arg.teamID, ret, arg.me) 601 if err != nil { 602 return nil, err 603 } 604 605 teamShim := func() *TeamShim { 606 return &TeamShim{Data: ret, Hidden: hiddenPackage.ChainData()} 607 } 608 609 // Determine whether to repoll merkle. 610 discardCache, repoll := l.load2DecideRepoll(mctx, arg, teamShim(), merklePolledAt) 611 if discardCache { 612 ret = nil 613 repoll = true 614 } 615 616 tracer.Stage("deepcopy") 617 if ret != nil { 618 // If we're pulling from a previous snapshot (that, let's say, we got from a shared cache), 619 // then make sure to DeepCopy() data out of it before we start mutating it below. We used 620 // to do this every step through the new links, but that was very expensive in terms of CPU 621 // for big teams, since it was hidden quadratic behavior. 622 tmp := ret.DeepCopy() 623 ret = &tmp 624 } else { 625 mctx.Debug("TeamLoader not using snapshot") 626 } 627 628 tracer.Stage("merkle") 629 var lastSeqno keybase1.Seqno 630 var lastLinkID keybase1.LinkID 631 var hiddenIsFresh bool 632 var lastMerkleRoot *libkb.MerkleRoot 633 634 // hiddenResp will be nill iff we do not make the merkleLookupWithHidden 635 // call. If the server does not return any hidden data, we will encode that 636 // as a non nil response whose RespType is MerkleHiddenResponseTypeNONE. 637 var hiddenResp *libkb.MerkleHiddenResponse 638 639 if (ret == nil) || repoll { 640 mctx.Debug("TeamLoader looking up merkle leaf (force:%v)", arg.forceRepoll) 641 642 // Reference the merkle tree to fetch the sigchain tail leaf for the team. 643 lastSeqno, lastLinkID, hiddenResp, lastMerkleRoot, err = l.world.merkleLookupWithHidden(ctx, arg.teamID, arg.public) 644 if err != nil { 645 return nil, err 646 } 647 mctx.Debug("received lastSeqno %v, lastLinkID %v", lastSeqno, lastLinkID) 648 649 hiddenIsFresh, err = l.checkHiddenResponse(mctx, hiddenPackage, hiddenResp) 650 if err != nil { 651 return nil, err 652 } 653 654 didRepoll = true 655 } else { 656 lastSeqno = ret.Chain.LastSeqno 657 lastLinkID = ret.Chain.LastLinkID 658 hiddenIsFresh = true 659 } 660 661 // For child calls to load2, the subteam reader ID is carried up 662 // or if it doesn't exist, start at this team. 663 readSubteamID := arg.teamID 664 if arg.readSubteamID != nil { 665 readSubteamID = *arg.readSubteamID 666 } 667 668 proofSet := newProofSet(l.G()) 669 var parentChildOperations []*parentChildOperation 670 671 // Backfill stubbed links that need to be filled now. 672 tracer.Stage("backfill") 673 var filledInStubbedLinks bool 674 if ret != nil && len(arg.needSeqnos) > 0 { 675 ret, proofSet, parentChildOperations, err = l.fillInStubbedLinks( 676 mctx, arg.me, arg.teamID, ret, arg.needSeqnos, readSubteamID, proofSet, parentChildOperations, lkc) 677 if err != nil { 678 return nil, err 679 } 680 filledInStubbedLinks = true 681 } 682 683 tracer.Stage("pre-fetch") 684 var fetchLinksAndOrSecrets bool 685 if ret == nil { 686 mctx.Debug("TeamLoader fetching: no cache") 687 // We have no cache 688 fetchLinksAndOrSecrets = true 689 } else if ret.Chain.LastSeqno < lastSeqno { 690 mctx.Debug("TeamLoader fetching: chain update") 691 // The cache is definitely behind 692 fetchLinksAndOrSecrets = true 693 } else if !hiddenIsFresh { 694 mctx.Debug("TeamLoader fetching: hidden chain wasn't fresh") 695 fetchLinksAndOrSecrets = true 696 } else if !l.hasSyncedSecrets(mctx, teamShim()) { 697 // The cached secrets are behind the cached chain. 698 // We may need to hit the server for secrets, even though there are no new links. 699 if arg.needAdmin { 700 mctx.Debug("TeamLoader fetching: NeedAdmin") 701 // Admins should always have up-to-date secrets. But not necessarily RKMs. 702 fetchLinksAndOrSecrets = true 703 } 704 if err := l.satisfiesNeedKeyGeneration(mctx, arg.needKeyGeneration, teamShim()); err != nil { 705 mctx.Debug("TeamLoader fetching: NeedKeyGeneration: %v", err) 706 fetchLinksAndOrSecrets = true 707 } 708 if err := l.satisfiesNeedsKBFSKeyGeneration(mctx, arg.needKBFSKeyGeneration, teamShim()); err != nil { 709 mctx.Debug("TeamLoader fetching: KBFSNeedKeyGeneration: %v", err) 710 fetchLinksAndOrSecrets = true 711 } 712 if arg.readSubteamID == nil { 713 // This is not a recursive load. We should have the keys. 714 // This may be an extra round trip for public teams you're not in. 715 mctx.Debug("TeamLoader fetching: primary load") 716 fetchLinksAndOrSecrets = true 717 } 718 } 719 // hasSyncedSecrets does not account for RKMs. So check RKM refreshers separeately. 720 721 if err := l.satisfiesNeedApplicationsAtGenerations(mctx, arg.needApplicationsAtGenerations, teamShim()); err != nil { 722 mctx.Debug("TeamLoader fetching: NeedApplicationsAtGenerations: %v", err) 723 fetchLinksAndOrSecrets = true 724 } 725 if err := l.satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx, 726 arg.needApplicationsAtGenerationsWithKBFS, teamShim()); err != nil { 727 mctx.Debug("TeamLoader fetching: NeedApplicationsAtGenerationsWithKBFS: %v", err) 728 fetchLinksAndOrSecrets = true 729 } 730 731 // Pull new links from the server 732 tracer.Stage("fetch") 733 var teamUpdate *rawTeam 734 if fetchLinksAndOrSecrets { 735 lows := l.lows(mctx, ret, hiddenPackage, arg) 736 mctx.Debug("TeamLoader getting links from server (%+v)", lows) 737 teamUpdate, err = l.world.getNewLinksFromServer(ctx, arg.teamID, lows, arg.readSubteamID) 738 if err != nil { 739 return nil, err 740 } 741 mctx.Debug("TeamLoader got %v links", len(teamUpdate.Chain)) 742 hiddenPackage.SetRatchetBlindingKeySet(teamUpdate.RatchetBlindingKeySet) 743 } 744 745 tracer.Stage("unpack") 746 links, err := teamUpdate.unpackLinks(mctx) 747 if err != nil { 748 return nil, err 749 } 750 var prev libkb.LinkID 751 if ret != nil { 752 prev, err = TeamSigChainState{inner: ret.Chain}.GetLatestLibkbLinkID() 753 if err != nil { 754 return nil, err 755 } 756 } 757 758 // A link which was signed by an admin. Sloppily the latest such link. 759 // Sloppy because this calculation misses out on e.g. a rotate_key signed by an admin. 760 // This value is used for skipping fullVerify on team.leave links, see `verifyLink`. 761 var fullVerifyCutoff keybase1.Seqno 762 for i := len(links) - 1; i >= 0; i-- { 763 if links[i].LinkType().RequiresAtLeastRole().IsAdminOrAbove() { 764 fullVerifyCutoff = links[i].Seqno() 765 break 766 } 767 } 768 if fullVerifyCutoff > 0 { 769 mctx.Debug("fullVerifyCutoff: %v", fullVerifyCutoff) 770 } 771 772 tracer.Stage("userPreload enable:%v parallel:%v wait:%v", 773 teamEnv.UserPreloadEnable, teamEnv.UserPreloadParallel, teamEnv.UserPreloadWait) 774 preloadCancel := l.userPreload(ctx, links, fullVerifyCutoff) 775 defer preloadCancel() 776 777 tracer.Stage("linkloop (%v)", len(links)) 778 parentsCache := make(parentChainCache) 779 780 // Don't log in the middle links if there are a great many links. 781 suppressLoggingStart := 5 782 suppressLoggingUpto := len(links) - 5 783 for i, link := range links { 784 var err error 785 ret, prev, err = l.doOneLink(mctx, arg, ret, hiddenPackage, link, i, suppressLoggingStart, suppressLoggingUpto, lastSeqno, &parentChildOperations, prev, fullVerifyCutoff, readSubteamID, proofSet, lkc, &parentsCache) 786 if err != nil { 787 return nil, err 788 } 789 } 790 791 if ret == nil { 792 return nil, fmt.Errorf("team loader fault: got nil from load2") 793 } 794 795 encKID, gen, role, err := l.hiddenPackageGetter(mctx, arg.teamID, ret, arg.me)() 796 if err != nil { 797 return nil, err 798 } 799 800 // If we did get an update from the server (hiddenResp != nil) are not a 801 // restricted bot AND this is not a recursive load (arg.readSubteamID == nil), 802 // then the server should have given us hidden chain data. 803 if hiddenResp != nil && hiddenResp.RespType == libkb.MerkleHiddenResponseTypeNONE && !role.IsRestrictedBot() && arg.readSubteamID == nil { 804 return nil, libkb.NewHiddenChainDataMissingError("Not a restricted bot or recursive load, but the server did not return merkle hidden chain data") 805 } 806 807 // Update the hidden package with team metadata once we process all of the 808 // links. This is necessary since we need the role to be up to date to know 809 // if we should skip seed checks on the hidden chain if we are loading as a 810 // RESTRICTEDBOT. 811 hiddenPackage.UpdateTeamMetadata(encKID, gen, role) 812 813 // Be sure to update the hidden chain after the main chain, since the latter can "ratchet" the former 814 err = hiddenPackage.Update(mctx, teamUpdate.GetHiddenChain(), hiddenResp.GetUncommittedSeqno()) 815 816 if err != nil { 817 return nil, err 818 } 819 err = hiddenPackage.CheckPTKsForDuplicates(mctx, func(g keybase1.PerTeamKeyGeneration) bool { 820 _, ok := ret.Chain.PerTeamKeys[g] 821 return ok 822 }) 823 if err != nil { 824 return nil, err 825 } 826 827 // The hidden team has pointers from the hidden chain up to the visible chain; check that they 828 // match the loaded team. We should have a full load of the team, so all parent pointers 829 // better hit their mark. 830 err = hiddenPackage.CheckParentPointersOnFullLoad(mctx, ret) 831 if err != nil { 832 return nil, err 833 } 834 835 preloadCancel() 836 if len(links) > 0 { 837 tbs.Log(ctx, "TeamLoader.verifyLink") 838 tbs.Log(ctx, "TeamLoader.applyNewLink") 839 tbs.Log(ctx, "SigChain.LoadFromServer.ReadAll") 840 tbs.Log(ctx, "loadKeyCache.loadKeyV2") 841 if teamEnv.Profile { 842 tbs.Log(ctx, "LoaderContextG.loadKeyV2") 843 tbs.Log(ctx, "CachedUPAKLoader.LoadKeyV2") // note LoadKeyV2 calls Load2 844 tbs.Log(ctx, "CachedUPAKLoader.LoadV2") 845 tbs.Log(ctx, "CachedUPAKLoader.DeepCopy") 846 mctx.Debug("TeamLoader lkc cache hits: %v", lkc.cacheHits) 847 } 848 } 849 850 if !ret.Chain.LastLinkID.Eq(lastLinkID) { 851 return nil, fmt.Errorf("wrong sigchain link ID: %v != %v", 852 ret.Chain.LastLinkID, lastLinkID) 853 } 854 855 if tailCheckRet != nil { 856 // If we previously discarded cache due to forceFullReload, or left the 857 // team, froze it, and are rejoining, make sure the previous tail is 858 // still in the chain. 859 // The chain loader ensures it is part of a well-formed chain with correct prevs. 860 linkID := ret.Chain.LinkIDs[tailCheckRet.Chain.LastSeqno] 861 if !linkID.Eq(tailCheckRet.Chain.LastLinkID) { 862 return nil, fmt.Errorf("got wrong sigchain link ID for seqno %d: expected %v from previous cache entry (frozen=%t); got %v in new chain", tailCheckRet.Chain.LastSeqno, 863 tailCheckRet.Chain.LastLinkID, ret.Frozen, linkID) 864 } 865 } 866 867 tracer.Stage("pco") 868 err = l.checkParentChildOperations(ctx, 869 arg.me, arg.teamID, ret.Chain.ParentID, readSubteamID, parentChildOperations, proofSet) 870 if err != nil { 871 return nil, err 872 } 873 874 tracer.Stage("checkproofs") 875 err = l.checkProofs(ctx, ret, proofSet) 876 if err != nil { 877 return nil, err 878 } 879 880 tracer.Stage("secrets") 881 if teamUpdate != nil { 882 if teamUpdate.SubteamReader { 883 // Only allow subteam-reader results if we are in a recursive load. 884 if arg.readSubteamID == nil { 885 return nil, fmt.Errorf("unexpected subteam reader result") 886 } 887 } else { 888 stateWrapper := newTeamSigChainState(teamShim()) 889 role, err := stateWrapper.GetUserRole(arg.me) 890 if err != nil { 891 role = keybase1.TeamRole_NONE 892 } 893 // Add the secrets. 894 // If it's a public team, there might not be secrets. (If we're not in the team) 895 // Restricted bots don't have any team secrets, so we also short circuit. 896 if !role.IsRestrictedBot() && (!ret.Chain.Public || (teamUpdate.Box != nil)) { 897 err = l.addSecrets(mctx, teamShim(), arg.me, teamUpdate.Box, teamUpdate.Prevs, teamUpdate.ReaderKeyMasks) 898 if err != nil { 899 return nil, pkgErrors.Wrap(err, "loading team secrets") 900 } 901 902 err = l.computeSeedChecks(ctx, ret) 903 if err != nil { 904 return nil, err 905 } 906 907 if teamUpdate.LegacyTLFUpgrade != nil { 908 err = l.addKBFSCryptKeys(mctx, teamShim(), teamUpdate.LegacyTLFUpgrade) 909 if err != nil { 910 return nil, fmt.Errorf("loading KBFS crypt keys: %v", err) 911 } 912 } 913 } 914 if role.IsRestrictedBot() { 915 // Clear out any secrets we may have had in memory if we were a 916 // previous role that had PTK access. 917 state := teamShim().MainChain() 918 state.PerTeamKeySeedsUnverified = make(map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKeySeedItem) 919 state.ReaderKeyMasks = make(map[keybase1.TeamApplication]map[keybase1.PerTeamKeyGeneration]keybase1.MaskB64) 920 state.TlfCryptKeys = make(map[keybase1.TeamApplication][]keybase1.CryptKey) 921 } 922 } 923 } 924 925 // Note that we might have done so just above after adding secrets, but before adding 926 // KBFS crypt keys. But it's cheap to run this method twice in a row. 927 tracer.Stage("computeSeedChecks") 928 err = l.computeSeedChecks(ctx, ret) 929 if err != nil { 930 return nil, err 931 } 932 933 if !arg.skipSeedCheck && arg.readSubteamID == nil { 934 err = hiddenPackage.CheckUpdatesAgainstSeedsWithMap(mctx, ret.PerTeamKeySeedsUnverified) 935 if err != nil { 936 return nil, err 937 } 938 } 939 940 // Make sure public works out 941 if ret.Chain.Public != arg.public { 942 return nil, fmt.Errorf("team public mismatch: chain:%v != arg:%v", ret.Chain.Public, arg.public) 943 } 944 if ret.Chain.Id.IsPublic() != ret.Chain.Public { 945 return nil, fmt.Errorf("team public mismatch: id:%v != chain:%v", ret.Chain.Id.IsPublic(), ret.Chain.Public) 946 } 947 948 // Sanity check the id 949 if !ret.Chain.Id.Eq(arg.teamID) { 950 return nil, fmt.Errorf("team id mismatch: %v != %v", ret.Chain.Id.String(), arg.teamID.String()) 951 } 952 953 // Recalculate the team name. 954 // This must always run to pick up changes on chain and off-chain with ancestor renames. 955 // Also because without this a subteam could claim any parent in its name. 956 tracer.Stage("namecalc") 957 newName, err := l.calculateName(ctx, ret, arg.me, readSubteamID, arg.staleOK) 958 if err != nil { 959 return nil, fmt.Errorf("error recalculating name for %v: %v", ret.Name, err) 960 } 961 if !ret.Name.Eq(newName) { 962 // This deep copy is an absurd price to pay, but these mid-team renames should be quite rare. 963 copy := ret.DeepCopy() 964 ret = © 965 ret.Name = newName 966 } 967 968 var needHiddenRotate bool 969 if !arg.skipNeedHiddenRotateCheck { 970 needHiddenRotate, err = l.checkNeedRotate(mctx, ret, arg.me, hiddenPackage) 971 if err != nil { 972 return nil, err 973 } 974 } 975 976 err = hiddenPackage.Commit(mctx) 977 if err != nil { 978 return nil, err 979 } 980 981 l.logIfUnsyncedSecrets(ctx, ret) 982 983 // Mutating this field is safe because only TeamLoader 984 // while holding the single-flight lock reads or writes this field. 985 ret.CachedAt = keybase1.ToTime(l.G().Clock().Now()) 986 987 // Clear the untrusted seqno hint. 988 // Mutating this field is safe because only TeamLoader 989 // while holding the single-flight lock reads or writes this field. 990 ret.LatestSeqnoHint = 0 991 992 if didRepoll { 993 tracer.Stage("audit") 994 auditMode := arg.auditMode 995 // in case of restricted bots or recursive loads, do not audit the 996 // hidden chain (as we might not have permission to see it). 997 if (role.IsRestrictedBot() || arg.readSubteamID != nil) && auditMode == keybase1.AuditMode_STANDARD { 998 auditMode = keybase1.AuditMode_STANDARD_NO_HIDDEN 999 } 1000 1001 err = l.audit(ctx, readSubteamID, &ret.Chain, hiddenPackage.ChainData(), lastMerkleRoot, auditMode) 1002 if err != nil { 1003 return nil, err 1004 } 1005 } else { 1006 mctx.Debug("Skipping audit in the TeamLoader as we did not repoll merkle") 1007 } 1008 1009 // Cache the validated result if it was actually updated via the team/get endpoint. In many cases, we're not 1010 // actually mutating the teams. Also, if we wound up filling in stubbed links, let's also restore the cache. 1011 if teamUpdate != nil || filledInStubbedLinks { 1012 tracer.Stage("put") 1013 l.storage.Put(mctx, ret) 1014 } 1015 1016 // If we wound up repolling the merkle tree for this team, say that we did. 1017 if didRepoll { 1018 l.merkleStorage.Put(mctx, arg.teamID, arg.public, keybase1.ToTime(mctx.G().Clock().Now())) 1019 } 1020 1021 tracer.Stage("notify") 1022 if cachedName != nil && !cachedName.Eq(newName) { 1023 chain := TeamSigChainState{inner: ret.Chain, hidden: hiddenPackage.ChainData()} 1024 // Send a notification if we used to have the name cached and it has changed at all. 1025 changeSet := keybase1.TeamChangeSet{Renamed: true} 1026 go l.G().NotifyRouter.HandleTeamChangedByID(context.Background(), chain.GetID(), chain.GetLatestSeqno(), 1027 chain.IsImplicit(), changeSet, chain.GetLatestHiddenSeqno(), keybase1.Seqno(0), keybase1.TeamChangedSource_LOCAL_RENAME) 1028 go l.G().NotifyRouter.HandleTeamChangedByName(context.Background(), cachedName.String(), chain.GetLatestSeqno(), 1029 chain.IsImplicit(), changeSet, chain.GetLatestHiddenSeqno(), keybase1.Seqno(0), keybase1.TeamChangedSource_LOCAL_RENAME) 1030 go l.G().NotifyRouter.HandleTeamChangedByName(context.Background(), newName.String(), chain.GetLatestSeqno(), 1031 chain.IsImplicit(), changeSet, chain.GetLatestHiddenSeqno(), keybase1.Seqno(0), keybase1.TeamChangedSource_LOCAL_RENAME) 1032 } 1033 1034 // Check request constraints 1035 tracer.Stage("postcheck") 1036 err = l.load2CheckReturn(mctx, arg, teamShim()) 1037 if err != nil { 1038 return nil, err 1039 } 1040 1041 load2res := load2ResT{ 1042 team: *ret, 1043 didRepoll: didRepoll, 1044 } 1045 1046 if hd := hiddenPackage.ChainData(); hd != nil { 1047 hd.NeedRotate = needHiddenRotate 1048 load2res.hidden = hd 1049 } 1050 1051 if needHiddenRotate { 1052 l.G().GetTeamBoxAuditor().MaybeScheduleDelayedBoxAuditTeam(mctx, arg.teamID) 1053 } 1054 1055 return &load2res, nil 1056 } 1057 1058 func (l *TeamLoader) hiddenPackageGetter(mctx libkb.MetaContext, id keybase1.TeamID, team *keybase1.TeamData, me keybase1.UserVersion) func() (encKID keybase1.KID, gen keybase1.PerTeamKeyGeneration, role keybase1.TeamRole, err error) { 1059 return func() (encKID keybase1.KID, gen keybase1.PerTeamKeyGeneration, 1060 role keybase1.TeamRole, err error) { 1061 if team == nil { 1062 return encKID, gen, keybase1.TeamRole_NONE, nil 1063 } 1064 state := TeamSigChainState{inner: team.Chain} 1065 1066 ptk, err := state.GetLatestPerTeamKey(mctx) 1067 if err != nil { 1068 return encKID, gen, keybase1.TeamRole_NONE, err 1069 } 1070 role, err = state.GetUserRole(me) 1071 if err != nil { 1072 return encKID, gen, keybase1.TeamRole_NONE, err 1073 } 1074 return ptk.EncKID, ptk.Gen, role, nil 1075 } 1076 } 1077 1078 func (l *TeamLoader) hiddenPackage(mctx libkb.MetaContext, id keybase1.TeamID, team *keybase1.TeamData, me keybase1.UserVersion) (ret *hidden.LoaderPackage, err error) { 1079 getter := l.hiddenPackageGetter(mctx, id, team, me) 1080 return hidden.NewLoaderPackage(mctx, id, getter) 1081 } 1082 1083 func (l *TeamLoader) isAllowedKeyerOf(mctx libkb.MetaContext, chain *keybase1.TeamData, me keybase1.UserVersion, them keybase1.UserVersion) (ret bool, err error) { 1084 state := TeamSigChainState{inner: chain.Chain} 1085 mctx = mctx.WithLogTag("IAKO") 1086 defer mctx.Trace(fmt.Sprintf("TeamLoader#isAllowedKeyerOf(%s, %s)", state.GetID(), them), &err)() 1087 1088 role, err := state.GetUserRole(them) 1089 if err != nil { 1090 return false, err 1091 } 1092 switch role { 1093 case keybase1.TeamRole_WRITER, keybase1.TeamRole_ADMIN, keybase1.TeamRole_OWNER: 1094 mctx.Debug("user fits explicit role (%s)", role) 1095 return true, nil 1096 } 1097 1098 if state.GetParentID() == nil { 1099 mctx.Debug("user is not an allowed keyer of the team") 1100 return false, nil 1101 } 1102 1103 // now check implict adminship 1104 yes, err := l.isImplicitAdminOf(mctx.Ctx(), state.GetID(), state.GetParentID(), me, them) 1105 if err != nil { 1106 return false, err 1107 } 1108 1109 if yes { 1110 mctx.Debug("user is an implicit admin of the team") 1111 return true, err 1112 } 1113 1114 mctx.Debug("user is not an allowed keyer of the team") 1115 1116 return false, nil 1117 1118 } 1119 1120 func (l *TeamLoader) checkNeedRotate(mctx libkb.MetaContext, chain *keybase1.TeamData, me keybase1.UserVersion, hiddenPackage *hidden.LoaderPackage) (ret bool, err error) { 1121 signer := hiddenPackage.LastReaderKeyRotator(mctx) 1122 if signer == nil { 1123 mctx.Debug("not checking need rotate, since last signer of hidden chain was nil") 1124 return false, nil 1125 } 1126 return l.checkNeedRotateWithSigner(mctx, chain, me, *signer) 1127 } 1128 1129 func (l *TeamLoader) checkNeedRotateWithSigner(mctx libkb.MetaContext, chain *keybase1.TeamData, me keybase1.UserVersion, signer keybase1.Signer) (ret bool, err error) { 1130 1131 defer mctx.Trace(fmt.Sprintf("TeamLoader::checkNeedRotateWithSigner(%+v)", signer), &err)() 1132 1133 uv := signer.UserVersion() 1134 1135 var isKeyer, amIKeyer bool 1136 1137 amIKeyer, err = l.isAllowedKeyerOf(mctx, chain, me, me) 1138 if err != nil { 1139 return false, err 1140 } 1141 if !amIKeyer { 1142 mctx.Debug("I am not a keyer for this team, so I can't rotate it even if required") 1143 return false, nil 1144 } 1145 1146 isKeyer, err = l.isAllowedKeyerOf(mctx, chain, me, uv) 1147 if err != nil { 1148 return false, err 1149 } 1150 1151 if !isKeyer { 1152 mctx.Debug("need rotate since %+v isn't an allowed keyer of the team", uv) 1153 return true, nil 1154 } 1155 1156 var found bool 1157 var revokedAt *keybase1.KeybaseTime 1158 1159 found, revokedAt, _, err = mctx.G().GetUPAKLoader().CheckKIDForUID(mctx.Ctx(), uv.Uid, signer.K) 1160 if err != nil { 1161 return false, err 1162 } 1163 1164 if !found || revokedAt != nil { 1165 var s string 1166 if revokedAt != nil { 1167 tm := revokedAt.Unix.Time() 1168 s = fmt.Sprintf(" (revoked at %s [%s ago])", tm, mctx.G().Clock().Now().Sub(tm)) 1169 } 1170 mctx.Debug("KID %s wasn't found for %+v%s", signer, s) 1171 return true, nil 1172 } 1173 1174 return false, nil 1175 } 1176 1177 func (l *TeamLoader) doOneLink(mctx libkb.MetaContext, arg load2ArgT, ret *keybase1.TeamData, hiddenPackage *hidden.LoaderPackage, link *ChainLinkUnpacked, i int, suppressLoggingStart int, suppressLoggingUpto int, lastSeqno keybase1.Seqno, parentChildOperations *[](*parentChildOperation), prev libkb.LinkID, fullVerifyCutoff keybase1.Seqno, readSubteamID keybase1.TeamID, proofSet *proofSetT, lkc *loadKeyCache, parentsCache *parentChainCache) (*keybase1.TeamData, libkb.LinkID, error) { 1178 1179 var nilPrev libkb.LinkID 1180 1181 ctx := mctx.Ctx() 1182 if suppressLoggingStart <= i && i < suppressLoggingUpto { 1183 if i == suppressLoggingStart { 1184 mctx.Debug("TeamLoader suppressing logs until %v", suppressLoggingUpto) 1185 } 1186 ctx = WithSuppressLogging(ctx, true) 1187 mctx = mctx.WithContext(ctx) 1188 } 1189 1190 if !ShouldSuppressLogging(ctx) { 1191 mctx.Debug("TeamLoader processing link seqno:%v", link.Seqno()) 1192 } 1193 1194 if link.Seqno() > lastSeqno { 1195 // This link came from a point in the chain after when we checked the merkle leaf. 1196 // Processing it would require re-checking merkle. 1197 // It would be tricky to ignore it because off-chain data is asserted to be in sync with the chain. 1198 // So, return an error that the caller will retry. 1199 mctx.Debug("TeamLoader found green link seqno:%v", link.Seqno()) 1200 return nil, nilPrev, NewGreenLinkError(link.Seqno()) 1201 } 1202 1203 if err := l.checkStubbed(ctx, arg, link); err != nil { 1204 return nil, nilPrev, err 1205 } 1206 1207 if !link.Prev().Eq(prev) { 1208 return nil, nilPrev, NewPrevError("team replay failed: prev chain broken at link %d (%v != %v)", 1209 i, link.Prev(), prev) 1210 } 1211 1212 if err := consumeRatchets(mctx, hiddenPackage, link); err != nil { 1213 return nil, nilPrev, err 1214 } 1215 1216 if err := checkPTKGenerationNotOnHiddenChain(mctx, hiddenPackage, link); err != nil { 1217 return nil, nilPrev, err 1218 } 1219 1220 var signer *SignerX 1221 var err error 1222 signer, err = l.verifyLink(ctx, arg.teamID, ret, arg.me, link, fullVerifyCutoff, 1223 readSubteamID, proofSet, lkc, *parentsCache) 1224 if err != nil { 1225 return nil, nilPrev, err 1226 } 1227 1228 if l.isParentChildOperation(ctx, link) { 1229 pco, err := l.toParentChildOperation(ctx, link) 1230 if err != nil { 1231 return nil, nilPrev, err 1232 } 1233 *parentChildOperations = append(*parentChildOperations, pco) 1234 } 1235 1236 ret, err = l.applyNewLink(ctx, ret, hiddenPackage.ChainData(), link, signer, arg.me) 1237 if err != nil { 1238 return nil, nilPrev, err 1239 } 1240 1241 return ret, link.LinkID(), nil 1242 } 1243 1244 // userPreload warms the upak cache with users who will probably need to be loaded to verify the chain. 1245 // Uses teamEnv and may be disabled. 1246 func (l *TeamLoader) userPreload(ctx context.Context, links []*ChainLinkUnpacked, fullVerifyCutoff keybase1.Seqno) (cancel func()) { 1247 ctx, cancel = context.WithCancel(ctx) 1248 if teamEnv.UserPreloadEnable { 1249 uidSet := make(map[keybase1.UID]struct{}) 1250 for _, link := range links { 1251 // fullVerify definition copied from verifyLink 1252 fullVerify := (link.LinkType() != libkb.SigchainV2TypeTeamLeave) || 1253 (link.Seqno() >= fullVerifyCutoff) || 1254 (link.source.EldestSeqno == 0) 1255 if !link.isStubbed() && fullVerify { 1256 uidSet[link.inner.Body.Key.UID] = struct{}{} 1257 } 1258 } 1259 l.G().Log.CDebugf(ctx, "TeamLoader userPreload uids: %v", len(uidSet)) 1260 if teamEnv.UserPreloadParallel { 1261 // Note this is full-parallel. Probably want pipelining if this is to be turned on by default. 1262 var wg sync.WaitGroup 1263 for uid := range uidSet { 1264 wg.Add(1) 1265 go func(uid keybase1.UID) { 1266 _, _, err := l.G().GetUPAKLoader().LoadV2( 1267 libkb.NewLoadUserArg(l.G()).WithUID(uid).WithPublicKeyOptional().WithNetContext(ctx)) 1268 if err != nil { 1269 l.G().Log.CDebugf(ctx, "error preloading uid %v", uid) 1270 } 1271 wg.Done() 1272 }(uid) 1273 } 1274 if teamEnv.UserPreloadWait { 1275 wg.Wait() 1276 } 1277 } else { 1278 for uid := range uidSet { 1279 _, _, err := l.G().GetUPAKLoader().LoadV2( 1280 libkb.NewLoadUserArg(l.G()).WithUID(uid).WithPublicKeyOptional().WithNetContext(ctx)) 1281 if err != nil { 1282 l.G().Log.CDebugf(ctx, "error preloading uid %v", uid) 1283 } 1284 } 1285 } 1286 } 1287 return cancel 1288 } 1289 1290 // Decide whether to repoll merkle based on load arg. 1291 // Returns (discardCache, repoll) 1292 // discardCache - the caller should throw out their cached copy and repoll. 1293 // repoll - hit up merkle for the latest tail 1294 // Considers: 1295 // - NeedAdmin 1296 // - NeedKeyGeneration 1297 // - NeedApplicationsAtGenerations 1298 // - WantMembers 1299 // - ForceRepoll 1300 // - Cache freshness / StaleOK 1301 // - NeedSeqnos 1302 // - JustUpdated 1303 // - If this user is in global "force repoll" mode, where it would be too spammy to 1304 // push out individual team changed notifications, so all team loads need a repoll. 1305 func (l *TeamLoader) load2DecideRepoll(mctx libkb.MetaContext, arg load2ArgT, fromCache Teamer, cachedPolledAt *keybase1.Time) (discardCache bool, repoll bool) { 1306 var reason string 1307 defer func() { 1308 if discardCache || repoll || reason != "" { 1309 mctx.Debug("load2DecideRepoll -> (discardCache:%v, repoll:%v) %v", discardCache, repoll, reason) 1310 } 1311 }() 1312 // NeedAdmin is a special constraint where we start from scratch. 1313 // Because of admin-only invite links. 1314 if arg.needAdmin { 1315 if !l.satisfiesNeedAdmin(mctx, arg.me, fromCache) { 1316 // Start from scratch if we are newly admin 1317 reason = "!satisfiesNeedAdmin" 1318 return true, true 1319 } 1320 } 1321 1322 if arg.forceRepoll { 1323 reason = "forceRepoll" 1324 return false, true 1325 } 1326 1327 // Repoll if the server has previously hinted that the team has new links. 1328 if fromCache != nil && fromCache.MainChain() != nil && fromCache.MainChain().Chain.LastSeqno < fromCache.MainChain().LatestSeqnoHint { 1329 reason = "behind seqno hint" 1330 return false, true 1331 } 1332 1333 if fromCache != nil && fromCache.HiddenChain() != nil && fromCache.HiddenChain().IsStale() { 1334 reason = "behind hidden seqno hint" 1335 return false, true 1336 } 1337 1338 // Repoll to get a new key generation 1339 if arg.needKeyGeneration > 0 { 1340 if err := l.satisfiesNeedKeyGeneration(mctx, arg.needKeyGeneration, fromCache); err != nil { 1341 reason = fmt.Sprintf("satisfiesNeedKeyGeneration -> %v", err) 1342 return false, true 1343 } 1344 } 1345 // Repoll to get new applications at generations 1346 if len(arg.needApplicationsAtGenerations) > 0 { 1347 if err := l.satisfiesNeedApplicationsAtGenerations(mctx, arg.needApplicationsAtGenerations, fromCache); err != nil { 1348 reason = fmt.Sprintf("satisfiesNeedApplicationsAtGenerations -> %v", err) 1349 return false, true 1350 } 1351 } 1352 if arg.needKBFSKeyGeneration.Generation > 0 { 1353 if err := l.satisfiesNeedsKBFSKeyGeneration(mctx, arg.needKBFSKeyGeneration, fromCache); err != nil { 1354 reason = fmt.Sprintf("satisfiesNeedsKBFSKeyGeneration -> %v", err) 1355 return false, true 1356 } 1357 } 1358 1359 if len(arg.needApplicationsAtGenerationsWithKBFS) > 0 { 1360 if err := l.satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx, 1361 arg.needApplicationsAtGenerationsWithKBFS, fromCache); err != nil { 1362 reason = fmt.Sprintf("satisfiesNeedApplicationsAtGenerationsWithKBFS -> %v", err) 1363 return false, true 1364 } 1365 } 1366 1367 // Repoll because it might help get the wanted members 1368 if len(arg.wantMembers) > 0 { 1369 if err := l.satisfiesWantMembers(mctx, arg.wantMembers, arg.wantMembersRole, fromCache); err != nil { 1370 reason = fmt.Sprintf("satisfiesWantMembers -> %v", err) 1371 return false, true 1372 } 1373 } 1374 1375 // Repoll if we need a seqno not in the cache. 1376 // Does not force a repoll if we just need to fill in previous links 1377 if len(arg.needSeqnos) > 0 { 1378 if fromCache == nil || fromCache.MainChain() == nil { 1379 reason = "need seqnos and no cache" 1380 return false, true 1381 } 1382 if fromCache.MainChain().Chain.LastSeqno < l.seqnosMax(arg.needSeqnos) { 1383 reason = "need seqnos" 1384 return false, true 1385 } 1386 } 1387 1388 if fromCache == nil || fromCache.MainChain() == nil { 1389 reason = "no cache" 1390 // We need a merkle leaf when starting from scratch. 1391 return false, true 1392 } 1393 1394 cachedAt := fromCache.MainChain().CachedAt 1395 if cachedPolledAt != nil && *cachedPolledAt > cachedAt { 1396 cachedAt = *cachedPolledAt 1397 } 1398 1399 cacheIsOld := !l.isFresh(mctx, cachedAt) 1400 if cacheIsOld && !arg.staleOK { 1401 // We need a merkle leaf 1402 reason = "cacheIsOld" 1403 return false, true 1404 } 1405 1406 // InForceRepoll needs to a acquire a lock, so avoid it by checking it last. 1407 if l.InForceRepollMode(mctx) { 1408 reason = "InForceRepollMode" 1409 return false, true 1410 } 1411 1412 return false, false 1413 } 1414 1415 // Check whether the load produced a snapshot that can be returned to the caller. 1416 // This should not check anything that is critical to the validity of the snapshot 1417 // because the snapshot is put into the cache before this check. 1418 // Considers: 1419 // - NeedAdmin 1420 // - NeedKeyGeneration 1421 // - NeedSeqnos 1422 func (l *TeamLoader) load2CheckReturn(mctx libkb.MetaContext, arg load2ArgT, shim Teamer) error { 1423 if arg.needAdmin { 1424 if !l.satisfiesNeedAdmin(mctx, arg.me, shim) { 1425 mctx.Debug("user %v is not an admin of team %v at seqno:%v", arg.me, arg.teamID, shim.MainChain().Chain.LastSeqno) 1426 return fmt.Errorf("user %v is not an admin of the team", arg.me) 1427 } 1428 } 1429 1430 // Repoll to get a new key generation 1431 if arg.needKeyGeneration > 0 { 1432 if err := l.satisfiesNeedKeyGeneration(mctx, arg.needKeyGeneration, shim); err != nil { 1433 return err 1434 } 1435 } 1436 if len(arg.needApplicationsAtGenerations) > 0 { 1437 if err := l.satisfiesNeedApplicationsAtGenerations(mctx, arg.needApplicationsAtGenerations, shim); err != nil { 1438 return err 1439 } 1440 } 1441 if arg.needKBFSKeyGeneration.Generation > 0 { 1442 if err := l.satisfiesNeedsKBFSKeyGeneration(mctx, arg.needKBFSKeyGeneration, shim); err != nil { 1443 return err 1444 } 1445 } 1446 if len(arg.needApplicationsAtGenerationsWithKBFS) > 0 { 1447 if err := l.satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx, arg.needApplicationsAtGenerationsWithKBFS, shim); err != nil { 1448 return err 1449 } 1450 } 1451 1452 if len(arg.needSeqnos) > 0 { 1453 if err := l.checkNeededSeqnos(mctx.Ctx(), shim.MainChain(), arg.needSeqnos); err != nil { 1454 return err 1455 } 1456 } 1457 1458 return nil 1459 } 1460 1461 // Whether the user is an admin at the snapshot, and there are no stubbed links, and keys are up to date. 1462 func (l *TeamLoader) satisfiesNeedAdmin(mctx libkb.MetaContext, me keybase1.UserVersion, team Teamer) bool { 1463 if team == nil || team.MainChain() == nil { 1464 return false 1465 } 1466 state := newTeamSigChainState(team) 1467 if state.HasAnyStubbedLinks() { 1468 return false 1469 } 1470 if !l.hasSyncedSecrets(mctx, team) { 1471 return false 1472 } 1473 role, err := state.GetUserRole(me) 1474 if err != nil { 1475 mctx.Debug("TeamLoader error getting my role: %v", err) 1476 return false 1477 } 1478 if !role.IsAdminOrAbove() { 1479 if !state.IsSubteam() { 1480 return false 1481 } 1482 yes, err := l.isImplicitAdminOf(mctx.Ctx(), state.GetID(), state.GetParentID(), me, me) 1483 if err != nil { 1484 mctx.Debug("TeamLoader error getting checking implicit admin: %s", err) 1485 return false 1486 } 1487 if !yes { 1488 return false 1489 } 1490 } 1491 return true 1492 } 1493 1494 // Check whether a user is an implicit admin of a team. 1495 func (l *TeamLoader) isImplicitAdminOf(ctx context.Context, teamID keybase1.TeamID, ancestorID *keybase1.TeamID, 1496 me keybase1.UserVersion, uv keybase1.UserVersion) (bool, error) { 1497 1498 // IDs of ancestors that were not freshly polled. 1499 // Check them again with forceRepoll if the affirmative is not found cached. 1500 checkAgain := make(map[keybase1.TeamID]bool) 1501 1502 check1 := func(chain *TeamSigChainState) bool { 1503 role, err := chain.GetUserRole(uv) 1504 if err != nil { 1505 return false 1506 } 1507 return role.IsAdminOrAbove() 1508 } 1509 1510 i := 0 1511 for { 1512 i++ 1513 if i >= 100 { 1514 // Break in case there's a bug in this loop. 1515 return false, fmt.Errorf("stuck in a loop while checking for implicit admin: %v", ancestorID) 1516 } 1517 1518 // Use load2 so that we can use subteam-reader and get secretless teams. 1519 ancestor, err := l.load2(ctx, load2ArgT{ 1520 teamID: *ancestorID, 1521 reason: "isImplicitAdminOf-1", 1522 me: me, 1523 readSubteamID: &teamID, 1524 }) 1525 if err != nil { 1526 return false, err 1527 } 1528 // Be wary, `ancestor` could be, and is likely, a secretless team. 1529 // Do not let it out of sight. 1530 ancestorChain := TeamSigChainState{inner: ancestor.team.Chain} 1531 1532 if !ancestor.didRepoll { 1533 checkAgain[ancestorChain.GetID()] = true 1534 } 1535 1536 if check1(&ancestorChain) { 1537 return true, nil 1538 } 1539 1540 if !ancestorChain.IsSubteam() { 1541 break 1542 } 1543 // Get the next level up. 1544 ancestorID = ancestorChain.GetParentID() 1545 } 1546 1547 // The answer was not found to be yes in the cache. 1548 // Try again with the teams that were not polled as they might have unseen updates. 1549 for ancestorID := range checkAgain { 1550 ancestor, err := l.load2(ctx, load2ArgT{ 1551 teamID: ancestorID, 1552 reason: "isImplicitAdminOf-again", 1553 me: me, 1554 forceRepoll: true, // Get the latest info. 1555 readSubteamID: &teamID, 1556 }) 1557 if err != nil { 1558 return false, err 1559 } 1560 // Be wary, `ancestor` could be, and is likely, a secretless team. 1561 // Do not let it out of sight. 1562 ancestorChain := TeamSigChainState{inner: ancestor.team.Chain} 1563 if check1(&ancestorChain) { 1564 return true, nil 1565 } 1566 } 1567 1568 return false, nil 1569 } 1570 1571 func (l *TeamLoader) satisfiesNeedsKBFSKeyGeneration(mctx libkb.MetaContext, 1572 kbfs keybase1.TeamKBFSKeyRefresher, state Teamer) error { 1573 if kbfs.Generation == 0 { 1574 return nil 1575 } 1576 if state == nil { 1577 return fmt.Errorf("nil team does not contain KBFS key generation: %#v", kbfs) 1578 } 1579 1580 gen, err := newTeamSigChainState(state).GetLatestKBFSGeneration(kbfs.AppType) 1581 if err != nil { 1582 return err 1583 } 1584 if kbfs.Generation > gen { 1585 return NewKBFSKeyGenerationError(kbfs.Generation, gen) 1586 } 1587 return nil 1588 } 1589 1590 // Whether the snapshot has loaded at least up to the key generation and has the secret. 1591 func (l *TeamLoader) satisfiesNeedKeyGeneration(mctx libkb.MetaContext, needKeyGeneration keybase1.PerTeamKeyGeneration, state Teamer) error { 1592 if needKeyGeneration == 0 { 1593 return nil 1594 } 1595 if state == nil { 1596 return fmt.Errorf("nil team does not contain key generation: %v", needKeyGeneration) 1597 } 1598 key, err := newTeamSigChainState(state).GetLatestPerTeamKey(mctx) 1599 if err != nil { 1600 return err 1601 } 1602 if needKeyGeneration > key.Gen { 1603 return fmt.Errorf("team key generation too low: %v < %v", key.Gen, needKeyGeneration) 1604 } 1605 _, ok := state.MainChain().PerTeamKeySeedsUnverified[needKeyGeneration] 1606 if !ok { 1607 return fmt.Errorf("team key secret missing for generation: %v", needKeyGeneration) 1608 } 1609 return nil 1610 } 1611 1612 // Whether the snapshot has loaded the reader key masks and key generations we 1613 // need. 1614 func (l *TeamLoader) satisfiesNeedApplicationsAtGenerations(mctx libkb.MetaContext, 1615 needApplicationsAtGenerations map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication, team Teamer) error { 1616 if len(needApplicationsAtGenerations) == 0 { 1617 return nil 1618 } 1619 if team == nil || team.MainChain() == nil { 1620 return fmt.Errorf("nil team does not contain applications: %v", needApplicationsAtGenerations) 1621 } 1622 for ptkGen, apps := range needApplicationsAtGenerations { 1623 for _, app := range apps { 1624 if _, err := ApplicationKeyAtGeneration(mctx, team, app, ptkGen); err != nil { 1625 return err 1626 } 1627 } 1628 } 1629 return nil 1630 } 1631 1632 func (l *TeamLoader) satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx libkb.MetaContext, 1633 needApplicationsAtGenerations map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication, 1634 state Teamer) error { 1635 if len(needApplicationsAtGenerations) == 0 { 1636 return nil 1637 } 1638 if state == nil || state.MainChain() == nil { 1639 return fmt.Errorf("nil team does not contain applications: %v", needApplicationsAtGenerations) 1640 } 1641 for ptkGen, apps := range needApplicationsAtGenerations { 1642 for _, app := range apps { 1643 if _, err := ApplicationKeyAtGenerationWithKBFS(mctx, state, app, ptkGen); err != nil { 1644 return err 1645 } 1646 } 1647 } 1648 return nil 1649 } 1650 1651 // Whether the snapshot has each of `wantMembers` as a member. 1652 func (l *TeamLoader) satisfiesWantMembers(mctx libkb.MetaContext, 1653 wantMembers []keybase1.UserVersion, wantMembersRole keybase1.TeamRole, state Teamer) error { 1654 1655 if wantMembersRole == keybase1.TeamRole_NONE { 1656 // Default to writer. 1657 wantMembersRole = keybase1.TeamRole_WRITER 1658 } 1659 if len(wantMembers) == 0 { 1660 return nil 1661 } 1662 if state == nil { 1663 return fmt.Errorf("nil team does not have wanted members") 1664 } 1665 for _, uv := range wantMembers { 1666 role, err := newTeamSigChainState(state).GetUserRole(uv) 1667 if err != nil { 1668 return fmt.Errorf("could not get wanted user role: %v", err) 1669 } 1670 if !role.IsOrAbove(wantMembersRole) { 1671 return fmt.Errorf("wanted user %v is a %v which is not at least %v", uv, role, wantMembersRole) 1672 } 1673 } 1674 return nil 1675 } 1676 1677 func (l *TeamLoader) mungeWantMembers(ctx context.Context, wantMembers []keybase1.UserVersion) (res []keybase1.UserVersion, err error) { 1678 for _, uv1 := range wantMembers { 1679 uv2 := uv1 1680 if uv2.EldestSeqno == 0 { 1681 // Lookup the latest eldest seqno for that uid. 1682 // This value may come from a cache. 1683 uv2.EldestSeqno, err = l.world.lookupEldestSeqno(ctx, uv2.Uid) 1684 if err != nil { 1685 return res, err 1686 } 1687 l.G().Log.CDebugf(ctx, "TeamLoader resolved wantMember %v -> %v", uv2.Uid, uv2.EldestSeqno) 1688 } 1689 res = append(res, uv2) 1690 } 1691 return res, err 1692 } 1693 1694 // Whether y is in xs. 1695 func (l *TeamLoader) seqnosContains(xs []keybase1.Seqno, y keybase1.Seqno) bool { 1696 for _, x := range xs { 1697 if x.Eq(y) { 1698 return true 1699 } 1700 } 1701 return false 1702 } 1703 1704 // Return the max in a list of positive seqnos. Returns 0 if the list is empty 1705 func (l *TeamLoader) seqnosMax(seqnos []keybase1.Seqno) (ret keybase1.Seqno) { 1706 for _, x := range seqnos { 1707 if x > ret { 1708 ret = x 1709 } 1710 } 1711 return ret 1712 } 1713 1714 // Whether a TeamData from the cache is fresh. 1715 func (l *TeamLoader) isFresh(mctx libkb.MetaContext, cachedAt keybase1.Time) bool { 1716 if cachedAt.IsZero() { 1717 // This should never happen. 1718 mctx.Warning("TeamLoader encountered zero cached time") 1719 return false 1720 } 1721 diff := mctx.G().Clock().Now().Sub(cachedAt.Time()) 1722 fresh := (diff <= freshnessLimit) 1723 if !fresh { 1724 mctx.Debug("TeamLoader cached snapshot is old: %v", diff) 1725 } 1726 return fresh 1727 } 1728 1729 // Whether the teams secrets are synced to the same point as its sigchain 1730 // Does not check RKMs. 1731 func (l *TeamLoader) hasSyncedSecrets(mctx libkb.MetaContext, team Teamer) bool { 1732 state := team.MainChain() 1733 n := len(team.MainChain().Chain.PerTeamKeys) 1734 offChainGen := len(state.PerTeamKeySeedsUnverified) 1735 mctx.Debug("TeamLoader#hasSyncedSecrets: found %d PTKs on the main chain (versus %d seeds)", n, offChainGen) 1736 if team.HiddenChain() != nil { 1737 m := len(team.HiddenChain().ReaderPerTeamKeys) 1738 mctx.Debug("TeamLoader#hasSyncedSecrets: found another %d PTKs on the hidden chain", m) 1739 n += m 1740 } 1741 return (n == offChainGen) 1742 } 1743 1744 func (l *TeamLoader) logIfUnsyncedSecrets(ctx context.Context, state *keybase1.TeamData) { 1745 onChainGen := keybase1.PerTeamKeyGeneration(len(state.Chain.PerTeamKeys)) 1746 offChainGen := keybase1.PerTeamKeyGeneration(len(state.PerTeamKeySeedsUnverified)) 1747 if onChainGen != offChainGen { 1748 l.G().Log.CDebugf(ctx, "TeamLoader unsynced secrets local:%v != chain:%v ", offChainGen, onChainGen) 1749 } 1750 } 1751 1752 func (l *TeamLoader) lows(mctx libkb.MetaContext, state *keybase1.TeamData, hp *hidden.LoaderPackage, arg load2ArgT) getLinksLows { 1753 var lows getLinksLows 1754 if state != nil { 1755 chain := TeamSigChainState{inner: state.Chain} 1756 lows.Seqno = chain.GetLatestSeqno() 1757 if !arg.foundRKMHole { 1758 lows.PerTeamKey = keybase1.PerTeamKeyGeneration(len(state.PerTeamKeySeedsUnverified)) 1759 } 1760 } 1761 if hp != nil { 1762 lows.HiddenChainSeqno = hp.LastFullSeqno() 1763 } 1764 return lows 1765 } 1766 1767 func (l *TeamLoader) OnLogout(mctx libkb.MetaContext) error { 1768 l.storage.ClearMem() 1769 return nil 1770 } 1771 1772 func (l *TeamLoader) OnDbNuke(mctx libkb.MetaContext) error { 1773 l.storage.ClearMem() 1774 return nil 1775 } 1776 1777 // Clear the in-memory cache. 1778 func (l *TeamLoader) ClearMem() { 1779 l.storage.ClearMem() 1780 } 1781 1782 func (l *TeamLoader) VerifyTeamName(ctx context.Context, id keybase1.TeamID, name keybase1.TeamName) error { 1783 if name.IsRootTeam() { 1784 if !name.ToTeamID(id.IsPublic()).Eq(id) { 1785 return NewResolveError(name, id) 1786 } 1787 return nil 1788 } 1789 teamData, _, err := l.Load(ctx, keybase1.LoadTeamArg{ 1790 ID: id, 1791 Public: id.IsPublic(), 1792 }) 1793 if err != nil { 1794 return err 1795 } 1796 gotName := teamData.Name 1797 if !gotName.Eq(name) { 1798 return NewResolveError(name, id) 1799 } 1800 return nil 1801 } 1802 1803 // List all the admins of ancestor teams. 1804 // Includes admins of the specified team only if they are also admins of ancestor teams. 1805 // The specified team must be a subteam, or an error is returned. 1806 // Always sends a flurry of RPCs to get the most up to date info. 1807 func (l *TeamLoader) ImplicitAdmins(ctx context.Context, teamID keybase1.TeamID) (impAdmins []keybase1.UserVersion, err error) { 1808 impAdminsMap := make(map[string]keybase1.UserVersion) // map to remove dups 1809 err = l.MapTeamAncestors(ctx, func(t keybase1.TeamSigChainState, _ keybase1.TeamName) error { 1810 ancestorChain := TeamSigChainState{inner: t} 1811 // Gather the admins. 1812 adminRoles := []keybase1.TeamRole{keybase1.TeamRole_OWNER, keybase1.TeamRole_ADMIN} 1813 for _, role := range adminRoles { 1814 uvs, err := ancestorChain.GetUsersWithRole(role) 1815 if err != nil { 1816 return err 1817 } 1818 for _, uv := range uvs { 1819 impAdminsMap[uv.String()] = uv 1820 } 1821 } 1822 return nil 1823 }, teamID, "implicitAdminsAncestor", func(keybase1.TeamSigChainState) bool { return true }) 1824 if err != nil { 1825 return nil, err 1826 } 1827 for _, uv := range impAdminsMap { 1828 impAdmins = append(impAdmins, uv) 1829 } 1830 return impAdmins, nil 1831 } 1832 1833 // MapTeamAncestors does NOT map over the team itself. 1834 func (l *TeamLoader) MapTeamAncestors( 1835 ctx context.Context, 1836 f func(keybase1.TeamSigChainState, keybase1.TeamName) error, 1837 teamID keybase1.TeamID, 1838 reason string, 1839 forceFullReloadOnceToAssert func(t keybase1.TeamSigChainState) bool, 1840 ) (err error) { 1841 initialTeamIdx := 0 1842 1843 me, err := l.world.getMe(ctx) 1844 if err != nil { 1845 return NewMapAncestorsError(err, initialTeamIdx) 1846 } 1847 1848 // Load the argument team 1849 team, _, err := l.load1(ctx, me, keybase1.LoadTeamArg{ 1850 ID: teamID, 1851 Public: teamID.IsPublic(), 1852 StaleOK: true, // We only use immutable fields. 1853 }) 1854 if err != nil { 1855 return NewMapAncestorsError(err, initialTeamIdx) 1856 } 1857 teamChain := TeamSigChainState{inner: team.Chain} 1858 if !teamChain.IsSubteam() { 1859 return NewMapAncestorsError( 1860 fmt.Errorf("cannot map over parents of a root team: %v", teamID), 1861 initialTeamIdx, 1862 ) 1863 } 1864 return l.mapTeamAncestorsHelper(ctx, f, teamID, teamChain.GetParentID(), reason, forceFullReloadOnceToAssert) 1865 } 1866 1867 func (l *TeamLoader) mapTeamAncestorsHelper( 1868 ctx context.Context, 1869 f func(keybase1.TeamSigChainState, keybase1.TeamName) error, 1870 teamID keybase1.TeamID, 1871 ancestorID *keybase1.TeamID, 1872 reason string, 1873 forceFullReloadOnceToAssert func(t keybase1.TeamSigChainState) bool, 1874 ) (err error) { 1875 i := 0 1876 1877 defer func() { 1878 if err != nil { 1879 err = NewMapAncestorsError(err, i) 1880 } 1881 }() 1882 1883 me, err := l.world.getMe(ctx) 1884 if err != nil { 1885 return err 1886 } 1887 1888 for { 1889 i++ 1890 if i >= 100 { 1891 // Break in case there's a bug in this loop. 1892 return fmt.Errorf("stuck in a loop while mapping over team parents: %v", ancestorID) 1893 } 1894 1895 load2Arg := load2ArgT{ 1896 teamID: *ancestorID, 1897 reason: reason, 1898 me: me, 1899 forceRepoll: true, // Get the latest info. 1900 readSubteamID: &teamID, 1901 } 1902 1903 var ancestor *load2ResT 1904 for { 1905 var err error 1906 // Use load2 so that we can use subteam-reader and get secretless teams. 1907 ancestor, err = l.load2(ctx, load2Arg) 1908 if err != nil { 1909 return err 1910 } 1911 1912 if forceFullReloadOnceToAssert == nil || 1913 forceFullReloadOnceToAssert(ancestor.team.Chain) { 1914 break 1915 } 1916 if load2Arg.forceFullReload { 1917 return fmt.Errorf("failed to assert predicate in ancestor %v after full force reload", ancestor.team.ID()) 1918 } 1919 load2Arg.forceFullReload = true 1920 } 1921 1922 // Be wary, `ancestor` could be, and is likely, a secretless team. 1923 // Do not let it out of sight. 1924 ancestorChain := TeamSigChainState{inner: ancestor.team.Chain} 1925 1926 err = f(ancestor.team.Chain, ancestor.team.Name) 1927 if err != nil { 1928 return err 1929 } 1930 1931 if !ancestorChain.IsSubteam() { 1932 break 1933 } 1934 // Get the next level up. 1935 ancestorID = ancestorChain.GetParentID() 1936 } 1937 1938 return nil 1939 } 1940 1941 func (l *TeamLoader) NotifyTeamRename(ctx context.Context, id keybase1.TeamID, newName string) error { 1942 // ignore newName from the server 1943 1944 // Load up the ancestor chain with ForceRepoll. 1945 // Then load down the ancestor chain without it (expect cache hits). 1946 // Not the most elegant way, but it will get the job done. 1947 // Each load on the way down will recalculate that team's name. 1948 1949 var ancestorIDs []keybase1.TeamID 1950 1951 me, err := l.world.getMe(ctx) 1952 if err != nil { 1953 return err 1954 } 1955 1956 loopID := &id 1957 for loopID != nil { 1958 load2Res, err := l.load2(ctx, load2ArgT{ 1959 teamID: *loopID, 1960 reason: "NotifyTeamRename-force", 1961 forceRepoll: true, 1962 readSubteamID: &id, 1963 me: me, 1964 }) 1965 if err != nil { 1966 return err 1967 } 1968 ancestorIDs = append(ancestorIDs, *loopID) 1969 chain := TeamSigChainState{inner: load2Res.team.Chain} 1970 if chain.IsSubteam() { 1971 loopID = chain.GetParentID() 1972 } else { 1973 loopID = nil 1974 } 1975 } 1976 1977 // reverse ancestorIDs so the root team appears first 1978 sort.SliceStable(ancestorIDs, func(i, j int) bool { return i > j }) 1979 1980 for _, loopID := range ancestorIDs { 1981 _, err := l.load2(ctx, load2ArgT{ 1982 teamID: loopID, 1983 reason: "NotifyTeamRename-quick", 1984 readSubteamID: &id, 1985 me: me, 1986 }) 1987 if err != nil { 1988 return err 1989 } 1990 } 1991 1992 return nil 1993 } 1994 1995 func (l *TeamLoader) getHeadMerkleSeqno(mctx libkb.MetaContext, readSubteamID keybase1.TeamID, state *keybase1.TeamSigChainState) (ret keybase1.Seqno, err error) { 1996 defer mctx.Trace("TeamLoader#getHeadMerkleSeqno", &err)() 1997 1998 if state.HeadMerkle != nil { 1999 return state.HeadMerkle.Seqno, nil 2000 } 2001 headSeqno := keybase1.Seqno(1) 2002 expectedLinkRaw, ok := state.LinkIDs[headSeqno] 2003 if !ok { 2004 return ret, fmt.Errorf("couldn't find head link in team state during audit") 2005 } 2006 expectedLink, err := libkb.ImportLinkID(expectedLinkRaw) 2007 if err != nil { 2008 return ret, err 2009 } 2010 teamUpdate, err := l.world.getLinksFromServer(mctx.Ctx(), state.Id, []keybase1.Seqno{headSeqno}, &readSubteamID) 2011 if err != nil { 2012 return ret, err 2013 } 2014 newLinks, err := teamUpdate.unpackLinks(mctx) 2015 if err != nil { 2016 return ret, err 2017 } 2018 if len(newLinks) != 1 { 2019 return ret, fmt.Errorf("expected only one chainlink back; got %d", len(newLinks)) 2020 } 2021 headLink := newLinks[0] 2022 err = headLink.AssertInnerOuterMatch() 2023 if err != nil { 2024 return ret, err 2025 } 2026 if headLink.Seqno() != headSeqno { 2027 return ret, NewInvalidLink(headLink, "wrong head seqno; wanted 1 but got something else") 2028 } 2029 if !headLink.LinkID().Eq(expectedLink) { 2030 return ret, NewInvalidLink(headLink, "wrong head link hash: %s != %s", headLink.LinkID(), expectedLink) 2031 } 2032 if headLink.isStubbed() { 2033 return ret, NewInvalidLink(headLink, "got a stubbed head link, but wasn't expecting that") 2034 } 2035 headMerkle := headLink.inner.Body.MerkleRoot.ToMerkleRootV2() 2036 state.HeadMerkle = &headMerkle 2037 return headMerkle.Seqno, nil 2038 } 2039 2040 func (l *TeamLoader) audit(ctx context.Context, readSubteamID keybase1.TeamID, state *keybase1.TeamSigChainState, hiddenChain *keybase1.HiddenTeamChain, lastMerkleRoot *libkb.MerkleRoot, auditMode keybase1.AuditMode) (err error) { 2041 mctx := libkb.NewMetaContext(ctx, l.G()) 2042 2043 if l.G().Env.Test.TeamSkipAudit { 2044 mctx.Debug("skipping audit in test due to flag") 2045 return nil 2046 } 2047 2048 headMerklSeqno, err := l.getHeadMerkleSeqno(mctx, readSubteamID, state) 2049 if err != nil { 2050 return err 2051 } 2052 2053 err = mctx.G().GetTeamAuditor().AuditTeam(mctx, state.Id, state.Public, headMerklSeqno, state.LinkIDs, hiddenChain.GetOuter(), state.LastSeqno, hiddenChain.GetLastCommittedSeqno(), lastMerkleRoot, auditMode) 2054 return err 2055 } 2056 2057 func (l *TeamLoader) ForceRepollUntil(ctx context.Context, dtime gregor.TimeOrOffset) error { 2058 l.G().Log.CDebugf(ctx, "TeamLoader#ForceRepollUntil(%+v)", dtime) 2059 l.forceRepollMutex.Lock() 2060 defer l.forceRepollMutex.Unlock() 2061 l.forceRepollUntil = dtime 2062 return nil 2063 } 2064 2065 func (l *TeamLoader) InForceRepollMode(mctx libkb.MetaContext) bool { 2066 l.forceRepollMutex.Lock() 2067 defer l.forceRepollMutex.Unlock() 2068 if l.forceRepollUntil == nil { 2069 return false 2070 } 2071 if !l.forceRepollUntil.Before(mctx.G().Clock().Now()) { 2072 mctx.Debug("TeamLoader#InForceRepollMode: returning true") 2073 return true 2074 } 2075 l.forceRepollUntil = nil 2076 return false 2077 }