1package teams 2 3import ( 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 22var teamEnv struct { 23 Profile bool 24 UserPreloadEnable bool 25 UserPreloadParallel bool 26 UserPreloadWait bool 27 ProofSetParallel bool 28} 29 30func 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 39const freshnessLimit = time.Duration(1) * time.Hour 40 41// Load a Team from the TeamLoader. 42// Can be called from inside the teams package. 43func 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. 67type 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 88var _ libkb.TeamLoader = (*TeamLoader)(nil) 89 90func 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. 102func 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 113func (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 124func 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 133func (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 151func (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 169func (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. 193func (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 207type nameLookupBurstCacheKey struct { 208 teamName keybase1.TeamName 209 public bool 210} 211 212func (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. 218func (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 245func 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 266func (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). 278func (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 398func (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. 418type 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 456type load2ResT struct { 457 team keybase1.TeamData 458 hidden *keybase1.HiddenTeamChain 459 didRepoll bool 460} 461 462func (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 468func (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 489func (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 498func (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 544func (l *TeamLoader) checkHiddenResponse(mctx libkb.MetaContext, hiddenPackage *hidden.LoaderPackage, hiddenResp *libkb.MerkleHiddenResponse) (hiddenIsFresh bool, err error) { 545 if hiddenResp.CommittedHiddenTail != nil { 546 mctx.Debug("hiddenResp: %+v UncommittedSeqno %+v CommittedSeqno %v", hiddenResp, hiddenResp.UncommittedSeqno, hiddenResp.CommittedHiddenTail.Seqno) 547 } else { 548 mctx.Debug("hiddenResp: %+v UncommittedSeqno %+v", hiddenResp, hiddenResp.UncommittedSeqno) 549 } 550 551 switch hiddenResp.RespType { 552 case libkb.MerkleHiddenResponseTypeNONE: 553 hiddenIsFresh = true 554 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).") 555 case libkb.MerkleHiddenResponseTypeFLAGOFF: 556 hiddenIsFresh = true 557 mctx.Debug("Skipping CheckHiddenMerklePathResponseAndAddRatchets as the hidden flag is off.") 558 default: 559 hiddenIsFresh, err = hiddenPackage.CheckHiddenMerklePathResponseAndAddRatchets(mctx, hiddenResp) 560 if err != nil { 561 return false, err 562 } 563 } 564 return hiddenIsFresh, nil 565} 566 567func (l *TeamLoader) load2InnerLockedRetry(ctx context.Context, arg load2ArgT) (*load2ResT, error) { 568 ctx, tbs := l.G().CTimeBuckets(ctx) 569 mctx := libkb.NewMetaContext(ctx, l.G()) 570 tracer := l.G().CTimeTracer(ctx, "TeamLoader.load2ILR", teamEnv.Profile) 571 defer tracer.Finish() 572 573 defer tbs.LogIfNonZero(ctx, "API.request") 574 575 var err error 576 var didRepoll bool 577 lkc := newLoadKeyCache() 578 579 // Fetch from cache 580 tracer.Stage("cache load") 581 tailCheckRet, frozen, tombstoned := l.storage.Get(mctx, arg.teamID, arg.public) 582 if tombstoned { 583 return nil, NewTeamTombstonedError() 584 } 585 586 // Fetch last polled time from merkle cache 587 merklePolledAt := l.merkleStorage.Get(mctx, arg.teamID, arg.public) 588 589 var ret *keybase1.TeamData 590 if !frozen && !arg.forceFullReload { 591 // Load from cache 592 ret = tailCheckRet 593 } 594 595 if ret != nil && !ret.Chain.Reader.Eq(arg.me) { 596 // Check that we are the same person as when this team was last loaded as a courtesy. 597 // This should never happen. We shouldn't be able to decrypt someone else's snapshot. 598 mctx.Warning("TeamLoader discarding snapshot for wrong user: (%v, %v) != (%v, %v)", 599 arg.me.Uid, arg.me.EldestSeqno, ret.Chain.Reader.Uid, ret.Chain.Reader.EldestSeqno) 600 ret = nil 601 } 602 603 var cachedName *keybase1.TeamName 604 if ret != nil && !ret.Name.IsNil() { 605 cachedName = &ret.Name 606 } 607 608 hiddenPackage, err := l.hiddenPackage(mctx, arg.teamID, ret, arg.me) 609 if err != nil { 610 return nil, err 611 } 612 613 teamShim := func() *TeamShim { 614 return &TeamShim{Data: ret, Hidden: hiddenPackage.ChainData()} 615 } 616 617 // Determine whether to repoll merkle. 618 discardCache, repoll := l.load2DecideRepoll(mctx, arg, teamShim(), merklePolledAt) 619 if discardCache { 620 ret = nil 621 repoll = true 622 } 623 624 tracer.Stage("deepcopy") 625 if ret != nil { 626 // If we're pulling from a previous snapshot (that, let's say, we got from a shared cache), 627 // then make sure to DeepCopy() data out of it before we start mutating it below. We used 628 // to do this every step through the new links, but that was very expensive in terms of CPU 629 // for big teams, since it was hidden quadratic behavior. 630 tmp := ret.DeepCopy() 631 ret = &tmp 632 } else { 633 mctx.Debug("TeamLoader not using snapshot") 634 } 635 636 tracer.Stage("merkle") 637 var lastSeqno keybase1.Seqno 638 var lastLinkID keybase1.LinkID 639 var hiddenIsFresh bool 640 var lastMerkleRoot *libkb.MerkleRoot 641 642 // hiddenResp will be nill iff we do not make the merkleLookupWithHidden 643 // call. If the server does not return any hidden data, we will encode that 644 // as a non nil response whose RespType is MerkleHiddenResponseTypeNONE. 645 var hiddenResp *libkb.MerkleHiddenResponse 646 647 if (ret == nil) || repoll { 648 mctx.Debug("TeamLoader looking up merkle leaf (force:%v)", arg.forceRepoll) 649 650 // Reference the merkle tree to fetch the sigchain tail leaf for the team. 651 lastSeqno, lastLinkID, hiddenResp, lastMerkleRoot, err = l.world.merkleLookupWithHidden(ctx, arg.teamID, arg.public) 652 if err != nil { 653 return nil, err 654 } 655 mctx.Debug("received lastSeqno %v, lastLinkID %v", lastSeqno, lastLinkID) 656 657 hiddenIsFresh, err = l.checkHiddenResponse(mctx, hiddenPackage, hiddenResp) 658 if err != nil { 659 return nil, err 660 } 661 662 didRepoll = true 663 } else { 664 lastSeqno = ret.Chain.LastSeqno 665 lastLinkID = ret.Chain.LastLinkID 666 hiddenIsFresh = true 667 } 668 669 // For child calls to load2, the subteam reader ID is carried up 670 // or if it doesn't exist, start at this team. 671 readSubteamID := arg.teamID 672 if arg.readSubteamID != nil { 673 readSubteamID = *arg.readSubteamID 674 } 675 676 proofSet := newProofSet(l.G()) 677 var parentChildOperations []*parentChildOperation 678 679 // Backfill stubbed links that need to be filled now. 680 tracer.Stage("backfill") 681 var filledInStubbedLinks bool 682 if ret != nil && len(arg.needSeqnos) > 0 { 683 ret, proofSet, parentChildOperations, err = l.fillInStubbedLinks( 684 mctx, arg.me, arg.teamID, ret, arg.needSeqnos, readSubteamID, proofSet, parentChildOperations, lkc) 685 if err != nil { 686 return nil, err 687 } 688 filledInStubbedLinks = true 689 } 690 691 tracer.Stage("pre-fetch") 692 var fetchLinksAndOrSecrets bool 693 if ret == nil { 694 mctx.Debug("TeamLoader fetching: no cache") 695 // We have no cache 696 fetchLinksAndOrSecrets = true 697 } else if ret.Chain.LastSeqno < lastSeqno { 698 mctx.Debug("TeamLoader fetching: chain update") 699 // The cache is definitely behind 700 fetchLinksAndOrSecrets = true 701 } else if !hiddenIsFresh { 702 mctx.Debug("TeamLoader fetching: hidden chain wasn't fresh") 703 fetchLinksAndOrSecrets = true 704 } else if !l.hasSyncedSecrets(mctx, teamShim()) { 705 // The cached secrets are behind the cached chain. 706 // We may need to hit the server for secrets, even though there are no new links. 707 if arg.needAdmin { 708 mctx.Debug("TeamLoader fetching: NeedAdmin") 709 // Admins should always have up-to-date secrets. But not necessarily RKMs. 710 fetchLinksAndOrSecrets = true 711 } 712 if err := l.satisfiesNeedKeyGeneration(mctx, arg.needKeyGeneration, teamShim()); err != nil { 713 mctx.Debug("TeamLoader fetching: NeedKeyGeneration: %v", err) 714 fetchLinksAndOrSecrets = true 715 } 716 if err := l.satisfiesNeedsKBFSKeyGeneration(mctx, arg.needKBFSKeyGeneration, teamShim()); err != nil { 717 mctx.Debug("TeamLoader fetching: KBFSNeedKeyGeneration: %v", err) 718 fetchLinksAndOrSecrets = true 719 } 720 if arg.readSubteamID == nil { 721 // This is not a recursive load. We should have the keys. 722 // This may be an extra round trip for public teams you're not in. 723 mctx.Debug("TeamLoader fetching: primary load") 724 fetchLinksAndOrSecrets = true 725 } 726 } 727 // hasSyncedSecrets does not account for RKMs. So check RKM refreshers separeately. 728 729 if err := l.satisfiesNeedApplicationsAtGenerations(mctx, arg.needApplicationsAtGenerations, teamShim()); err != nil { 730 mctx.Debug("TeamLoader fetching: NeedApplicationsAtGenerations: %v", err) 731 fetchLinksAndOrSecrets = true 732 } 733 if err := l.satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx, 734 arg.needApplicationsAtGenerationsWithKBFS, teamShim()); err != nil { 735 mctx.Debug("TeamLoader fetching: NeedApplicationsAtGenerationsWithKBFS: %v", err) 736 fetchLinksAndOrSecrets = true 737 } 738 739 // Pull new links from the server 740 tracer.Stage("fetch") 741 var teamUpdate *rawTeam 742 if fetchLinksAndOrSecrets { 743 lows := l.lows(mctx, ret, hiddenPackage, arg) 744 mctx.Debug("TeamLoader getting links from server (%+v)", lows) 745 teamUpdate, err = l.world.getNewLinksFromServer(ctx, arg.teamID, lows, arg.readSubteamID) 746 if err != nil { 747 return nil, err 748 } 749 mctx.Debug("TeamLoader got %v links", len(teamUpdate.Chain)) 750 hiddenPackage.SetRatchetBlindingKeySet(teamUpdate.RatchetBlindingKeySet) 751 } 752 753 tracer.Stage("unpack") 754 links, err := teamUpdate.unpackLinks(mctx) 755 if err != nil { 756 return nil, err 757 } 758 var prev libkb.LinkID 759 if ret != nil { 760 prev, err = TeamSigChainState{inner: ret.Chain}.GetLatestLibkbLinkID() 761 if err != nil { 762 return nil, err 763 } 764 } 765 766 // A link which was signed by an admin. Sloppily the latest such link. 767 // Sloppy because this calculation misses out on e.g. a rotate_key signed by an admin. 768 // This value is used for skipping fullVerify on team.leave links, see `verifyLink`. 769 var fullVerifyCutoff keybase1.Seqno 770 for i := len(links) - 1; i >= 0; i-- { 771 if links[i].LinkType().RequiresAtLeastRole().IsAdminOrAbove() { 772 fullVerifyCutoff = links[i].Seqno() 773 break 774 } 775 } 776 if fullVerifyCutoff > 0 { 777 mctx.Debug("fullVerifyCutoff: %v", fullVerifyCutoff) 778 } 779 780 tracer.Stage("userPreload enable:%v parallel:%v wait:%v", 781 teamEnv.UserPreloadEnable, teamEnv.UserPreloadParallel, teamEnv.UserPreloadWait) 782 preloadCancel := l.userPreload(ctx, links, fullVerifyCutoff) 783 defer preloadCancel() 784 785 tracer.Stage("linkloop (%v)", len(links)) 786 parentsCache := make(parentChainCache) 787 788 // Don't log in the middle links if there are a great many links. 789 suppressLoggingStart := 5 790 suppressLoggingUpto := len(links) - 5 791 for i, link := range links { 792 var err error 793 ret, prev, err = l.doOneLink(mctx, arg, ret, hiddenPackage, link, i, suppressLoggingStart, suppressLoggingUpto, lastSeqno, &parentChildOperations, prev, fullVerifyCutoff, readSubteamID, proofSet, lkc, &parentsCache) 794 if err != nil { 795 return nil, err 796 } 797 } 798 799 if ret == nil { 800 return nil, fmt.Errorf("team loader fault: got nil from load2") 801 } 802 803 encKID, gen, role, err := l.hiddenPackageGetter(mctx, arg.teamID, ret, arg.me)() 804 if err != nil { 805 return nil, err 806 } 807 808 // If we did get an update from the server (hiddenResp != nil) are not a 809 // restricted bot AND this is not a recursive load (arg.readSubteamID == nil), 810 // then the server should have given us hidden chain data. 811 if hiddenResp != nil && hiddenResp.RespType == libkb.MerkleHiddenResponseTypeNONE && !role.IsRestrictedBot() && arg.readSubteamID == nil { 812 return nil, libkb.NewHiddenChainDataMissingError("Not a restricted bot or recursive load, but the server did not return merkle hidden chain data") 813 } 814 815 // Update the hidden package with team metadata once we process all of the 816 // links. This is necessary since we need the role to be up to date to know 817 // if we should skip seed checks on the hidden chain if we are loading as a 818 // RESTRICTEDBOT. 819 hiddenPackage.UpdateTeamMetadata(encKID, gen, role) 820 821 // Be sure to update the hidden chain after the main chain, since the latter can "ratchet" the former 822 err = hiddenPackage.Update(mctx, teamUpdate.GetHiddenChain(), hiddenResp.GetUncommittedSeqno()) 823 824 if err != nil { 825 return nil, err 826 } 827 err = hiddenPackage.CheckPTKsForDuplicates(mctx, func(g keybase1.PerTeamKeyGeneration) bool { 828 _, ok := ret.Chain.PerTeamKeys[g] 829 return ok 830 }) 831 if err != nil { 832 return nil, err 833 } 834 835 // The hidden team has pointers from the hidden chain up to the visible chain; check that they 836 // match the loaded team. We should have a full load of the team, so all parent pointers 837 // better hit their mark. 838 err = hiddenPackage.CheckParentPointersOnFullLoad(mctx, ret) 839 if err != nil { 840 return nil, err 841 } 842 843 preloadCancel() 844 if len(links) > 0 { 845 tbs.Log(ctx, "TeamLoader.verifyLink") 846 tbs.Log(ctx, "TeamLoader.applyNewLink") 847 tbs.Log(ctx, "SigChain.LoadFromServer.ReadAll") 848 tbs.Log(ctx, "loadKeyCache.loadKeyV2") 849 if teamEnv.Profile { 850 tbs.Log(ctx, "LoaderContextG.loadKeyV2") 851 tbs.Log(ctx, "CachedUPAKLoader.LoadKeyV2") // note LoadKeyV2 calls Load2 852 tbs.Log(ctx, "CachedUPAKLoader.LoadV2") 853 tbs.Log(ctx, "CachedUPAKLoader.DeepCopy") 854 mctx.Debug("TeamLoader lkc cache hits: %v", lkc.cacheHits) 855 } 856 } 857 858 if !ret.Chain.LastLinkID.Eq(lastLinkID) { 859 return nil, fmt.Errorf("wrong sigchain link ID: %v != %v", 860 ret.Chain.LastLinkID, lastLinkID) 861 } 862 863 if tailCheckRet != nil { 864 // If we previously discarded cache due to forceFullReload, or left the 865 // team, froze it, and are rejoining, make sure the previous tail is 866 // still in the chain. 867 // The chain loader ensures it is part of a well-formed chain with correct prevs. 868 linkID := ret.Chain.LinkIDs[tailCheckRet.Chain.LastSeqno] 869 if !linkID.Eq(tailCheckRet.Chain.LastLinkID) { 870 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, 871 tailCheckRet.Chain.LastLinkID, ret.Frozen, linkID) 872 } 873 } 874 875 tracer.Stage("pco") 876 err = l.checkParentChildOperations(ctx, 877 arg.me, arg.teamID, ret.Chain.ParentID, readSubteamID, parentChildOperations, proofSet) 878 if err != nil { 879 return nil, err 880 } 881 882 tracer.Stage("checkproofs") 883 err = l.checkProofs(ctx, ret, proofSet) 884 if err != nil { 885 return nil, err 886 } 887 888 tracer.Stage("secrets") 889 if teamUpdate != nil { 890 if teamUpdate.SubteamReader { 891 // Only allow subteam-reader results if we are in a recursive load. 892 if arg.readSubteamID == nil { 893 return nil, fmt.Errorf("unexpected subteam reader result") 894 } 895 } else { 896 stateWrapper := newTeamSigChainState(teamShim()) 897 role, err := stateWrapper.GetUserRole(arg.me) 898 if err != nil { 899 role = keybase1.TeamRole_NONE 900 } 901 // Add the secrets. 902 // If it's a public team, there might not be secrets. (If we're not in the team) 903 // Restricted bots don't have any team secrets, so we also short circuit. 904 if !role.IsRestrictedBot() && (!ret.Chain.Public || (teamUpdate.Box != nil)) { 905 err = l.addSecrets(mctx, teamShim(), arg.me, teamUpdate.Box, teamUpdate.Prevs, teamUpdate.ReaderKeyMasks) 906 if err != nil { 907 return nil, pkgErrors.Wrap(err, "loading team secrets") 908 } 909 910 err = l.computeSeedChecks(ctx, ret) 911 if err != nil { 912 return nil, err 913 } 914 915 if teamUpdate.LegacyTLFUpgrade != nil { 916 err = l.addKBFSCryptKeys(mctx, teamShim(), teamUpdate.LegacyTLFUpgrade) 917 if err != nil { 918 return nil, fmt.Errorf("loading KBFS crypt keys: %v", err) 919 } 920 } 921 } 922 if role.IsRestrictedBot() { 923 // Clear out any secrets we may have had in memory if we were a 924 // previous role that had PTK access. 925 state := teamShim().MainChain() 926 state.PerTeamKeySeedsUnverified = make(map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKeySeedItem) 927 state.ReaderKeyMasks = make(map[keybase1.TeamApplication]map[keybase1.PerTeamKeyGeneration]keybase1.MaskB64) 928 state.TlfCryptKeys = make(map[keybase1.TeamApplication][]keybase1.CryptKey) 929 } 930 } 931 } 932 933 // Note that we might have done so just above after adding secrets, but before adding 934 // KBFS crypt keys. But it's cheap to run this method twice in a row. 935 tracer.Stage("computeSeedChecks") 936 err = l.computeSeedChecks(ctx, ret) 937 if err != nil { 938 return nil, err 939 } 940 941 if !arg.skipSeedCheck && arg.readSubteamID == nil { 942 err = hiddenPackage.CheckUpdatesAgainstSeedsWithMap(mctx, ret.PerTeamKeySeedsUnverified) 943 if err != nil { 944 return nil, err 945 } 946 } 947 948 // Make sure public works out 949 if ret.Chain.Public != arg.public { 950 return nil, fmt.Errorf("team public mismatch: chain:%v != arg:%v", ret.Chain.Public, arg.public) 951 } 952 if ret.Chain.Id.IsPublic() != ret.Chain.Public { 953 return nil, fmt.Errorf("team public mismatch: id:%v != chain:%v", ret.Chain.Id.IsPublic(), ret.Chain.Public) 954 } 955 956 // Sanity check the id 957 if !ret.Chain.Id.Eq(arg.teamID) { 958 return nil, fmt.Errorf("team id mismatch: %v != %v", ret.Chain.Id.String(), arg.teamID.String()) 959 } 960 961 // Recalculate the team name. 962 // This must always run to pick up changes on chain and off-chain with ancestor renames. 963 // Also because without this a subteam could claim any parent in its name. 964 tracer.Stage("namecalc") 965 newName, err := l.calculateName(ctx, ret, arg.me, readSubteamID, arg.staleOK) 966 if err != nil { 967 return nil, fmt.Errorf("error recalculating name for %v: %v", ret.Name, err) 968 } 969 if !ret.Name.Eq(newName) { 970 // This deep copy is an absurd price to pay, but these mid-team renames should be quite rare. 971 copy := ret.DeepCopy() 972 ret = © 973 ret.Name = newName 974 } 975 976 var needHiddenRotate bool 977 if !arg.skipNeedHiddenRotateCheck { 978 needHiddenRotate, err = l.checkNeedRotate(mctx, ret, arg.me, hiddenPackage) 979 if err != nil { 980 return nil, err 981 } 982 } 983 984 err = hiddenPackage.Commit(mctx) 985 if err != nil { 986 return nil, err 987 } 988 989 l.logIfUnsyncedSecrets(ctx, ret) 990 991 // Mutating this field is safe because only TeamLoader 992 // while holding the single-flight lock reads or writes this field. 993 ret.CachedAt = keybase1.ToTime(l.G().Clock().Now()) 994 995 // Clear the untrusted seqno hint. 996 // Mutating this field is safe because only TeamLoader 997 // while holding the single-flight lock reads or writes this field. 998 ret.LatestSeqnoHint = 0 999 1000 if didRepoll { 1001 tracer.Stage("audit") 1002 auditMode := arg.auditMode 1003 // in case of restricted bots or recursive loads, do not audit the 1004 // hidden chain (as we might not have permission to see it). 1005 if (role.IsRestrictedBot() || arg.readSubteamID != nil) && auditMode == keybase1.AuditMode_STANDARD { 1006 auditMode = keybase1.AuditMode_STANDARD_NO_HIDDEN 1007 } 1008 1009 err = l.audit(ctx, readSubteamID, &ret.Chain, hiddenPackage.ChainData(), lastMerkleRoot, auditMode) 1010 if err != nil { 1011 return nil, err 1012 } 1013 } else { 1014 mctx.Debug("Skipping audit in the TeamLoader as we did not repoll merkle") 1015 } 1016 1017 // Cache the validated result if it was actually updated via the team/get endpoint. In many cases, we're not 1018 // actually mutating the teams. Also, if we wound up filling in stubbed links, let's also restore the cache. 1019 if teamUpdate != nil || filledInStubbedLinks { 1020 tracer.Stage("put") 1021 l.storage.Put(mctx, ret) 1022 } 1023 1024 // If we wound up repolling the merkle tree for this team, say that we did. 1025 if didRepoll { 1026 l.merkleStorage.Put(mctx, arg.teamID, arg.public, keybase1.ToTime(mctx.G().Clock().Now())) 1027 } 1028 1029 tracer.Stage("notify") 1030 if cachedName != nil && !cachedName.Eq(newName) { 1031 chain := TeamSigChainState{inner: ret.Chain, hidden: hiddenPackage.ChainData()} 1032 // Send a notification if we used to have the name cached and it has changed at all. 1033 changeSet := keybase1.TeamChangeSet{Renamed: true} 1034 go l.G().NotifyRouter.HandleTeamChangedByID(context.Background(), chain.GetID(), chain.GetLatestSeqno(), 1035 chain.IsImplicit(), changeSet, chain.GetLatestHiddenSeqno(), keybase1.Seqno(0), keybase1.TeamChangedSource_LOCAL_RENAME) 1036 go l.G().NotifyRouter.HandleTeamChangedByName(context.Background(), cachedName.String(), chain.GetLatestSeqno(), 1037 chain.IsImplicit(), changeSet, chain.GetLatestHiddenSeqno(), keybase1.Seqno(0), keybase1.TeamChangedSource_LOCAL_RENAME) 1038 go l.G().NotifyRouter.HandleTeamChangedByName(context.Background(), newName.String(), chain.GetLatestSeqno(), 1039 chain.IsImplicit(), changeSet, chain.GetLatestHiddenSeqno(), keybase1.Seqno(0), keybase1.TeamChangedSource_LOCAL_RENAME) 1040 } 1041 1042 // Check request constraints 1043 tracer.Stage("postcheck") 1044 err = l.load2CheckReturn(mctx, arg, teamShim()) 1045 if err != nil { 1046 return nil, err 1047 } 1048 1049 load2res := load2ResT{ 1050 team: *ret, 1051 didRepoll: didRepoll, 1052 } 1053 1054 if hd := hiddenPackage.ChainData(); hd != nil { 1055 hd.NeedRotate = needHiddenRotate 1056 load2res.hidden = hd 1057 } 1058 1059 if needHiddenRotate { 1060 l.G().GetTeamBoxAuditor().MaybeScheduleDelayedBoxAuditTeam(mctx, arg.teamID) 1061 } 1062 1063 return &load2res, nil 1064} 1065 1066func (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) { 1067 return func() (encKID keybase1.KID, gen keybase1.PerTeamKeyGeneration, 1068 role keybase1.TeamRole, err error) { 1069 if team == nil { 1070 return encKID, gen, keybase1.TeamRole_NONE, nil 1071 } 1072 state := TeamSigChainState{inner: team.Chain} 1073 1074 ptk, err := state.GetLatestPerTeamKey(mctx) 1075 if err != nil { 1076 return encKID, gen, keybase1.TeamRole_NONE, err 1077 } 1078 role, err = state.GetUserRole(me) 1079 if err != nil { 1080 return encKID, gen, keybase1.TeamRole_NONE, err 1081 } 1082 return ptk.EncKID, ptk.Gen, role, nil 1083 } 1084} 1085 1086func (l *TeamLoader) hiddenPackage(mctx libkb.MetaContext, id keybase1.TeamID, team *keybase1.TeamData, me keybase1.UserVersion) (ret *hidden.LoaderPackage, err error) { 1087 getter := l.hiddenPackageGetter(mctx, id, team, me) 1088 return hidden.NewLoaderPackage(mctx, id, getter) 1089} 1090 1091func (l *TeamLoader) isAllowedKeyerOf(mctx libkb.MetaContext, chain *keybase1.TeamData, me keybase1.UserVersion, them keybase1.UserVersion) (ret bool, err error) { 1092 state := TeamSigChainState{inner: chain.Chain} 1093 mctx = mctx.WithLogTag("IAKO") 1094 defer mctx.Trace(fmt.Sprintf("TeamLoader#isAllowedKeyerOf(%s, %s)", state.GetID(), them), &err)() 1095 1096 role, err := state.GetUserRole(them) 1097 if err != nil { 1098 return false, err 1099 } 1100 switch role { 1101 case keybase1.TeamRole_WRITER, keybase1.TeamRole_ADMIN, keybase1.TeamRole_OWNER: 1102 mctx.Debug("user fits explicit role (%s)", role) 1103 return true, nil 1104 } 1105 1106 if state.GetParentID() == nil { 1107 mctx.Debug("user is not an allowed keyer of the team") 1108 return false, nil 1109 } 1110 1111 // now check implict adminship 1112 yes, err := l.isImplicitAdminOf(mctx.Ctx(), state.GetID(), state.GetParentID(), me, them) 1113 if err != nil { 1114 return false, err 1115 } 1116 1117 if yes { 1118 mctx.Debug("user is an implicit admin of the team") 1119 return true, err 1120 } 1121 1122 mctx.Debug("user is not an allowed keyer of the team") 1123 1124 return false, nil 1125 1126} 1127 1128func (l *TeamLoader) checkNeedRotate(mctx libkb.MetaContext, chain *keybase1.TeamData, me keybase1.UserVersion, hiddenPackage *hidden.LoaderPackage) (ret bool, err error) { 1129 signer := hiddenPackage.LastReaderKeyRotator(mctx) 1130 if signer == nil { 1131 mctx.Debug("not checking need rotate, since last signer of hidden chain was nil") 1132 return false, nil 1133 } 1134 return l.checkNeedRotateWithSigner(mctx, chain, me, *signer) 1135} 1136 1137func (l *TeamLoader) checkNeedRotateWithSigner(mctx libkb.MetaContext, chain *keybase1.TeamData, me keybase1.UserVersion, signer keybase1.Signer) (ret bool, err error) { 1138 1139 defer mctx.Trace(fmt.Sprintf("TeamLoader::checkNeedRotateWithSigner(%+v)", signer), &err)() 1140 1141 uv := signer.UserVersion() 1142 1143 var isKeyer, amIKeyer bool 1144 1145 amIKeyer, err = l.isAllowedKeyerOf(mctx, chain, me, me) 1146 if err != nil { 1147 return false, err 1148 } 1149 if !amIKeyer { 1150 mctx.Debug("I am not a keyer for this team, so I can't rotate it even if required") 1151 return false, nil 1152 } 1153 1154 isKeyer, err = l.isAllowedKeyerOf(mctx, chain, me, uv) 1155 if err != nil { 1156 return false, err 1157 } 1158 1159 if !isKeyer { 1160 mctx.Debug("need rotate since %+v isn't an allowed keyer of the team", uv) 1161 return true, nil 1162 } 1163 1164 var found bool 1165 var revokedAt *keybase1.KeybaseTime 1166 1167 found, revokedAt, _, err = mctx.G().GetUPAKLoader().CheckKIDForUID(mctx.Ctx(), uv.Uid, signer.K) 1168 if err != nil { 1169 return false, err 1170 } 1171 1172 if !found || revokedAt != nil { 1173 var s string 1174 if revokedAt != nil { 1175 tm := revokedAt.Unix.Time() 1176 s = fmt.Sprintf(" (revoked at %s [%s ago])", tm, mctx.G().Clock().Now().Sub(tm)) 1177 } 1178 mctx.Debug("KID %s wasn't found for %+v%s", signer, s) 1179 return true, nil 1180 } 1181 1182 return false, nil 1183} 1184 1185func (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) { 1186 1187 var nilPrev libkb.LinkID 1188 1189 ctx := mctx.Ctx() 1190 if suppressLoggingStart <= i && i < suppressLoggingUpto { 1191 if i == suppressLoggingStart { 1192 mctx.Debug("TeamLoader suppressing logs until %v", suppressLoggingUpto) 1193 } 1194 ctx = WithSuppressLogging(ctx, true) 1195 mctx = mctx.WithContext(ctx) 1196 } 1197 1198 if !ShouldSuppressLogging(ctx) { 1199 mctx.Debug("TeamLoader processing link seqno:%v", link.Seqno()) 1200 } 1201 1202 if link.Seqno() > lastSeqno { 1203 // This link came from a point in the chain after when we checked the merkle leaf. 1204 // Processing it would require re-checking merkle. 1205 // It would be tricky to ignore it because off-chain data is asserted to be in sync with the chain. 1206 // So, return an error that the caller will retry. 1207 mctx.Debug("TeamLoader found green link seqno:%v", link.Seqno()) 1208 return nil, nilPrev, NewGreenLinkError(link.Seqno()) 1209 } 1210 1211 if err := l.checkStubbed(ctx, arg, link); err != nil { 1212 return nil, nilPrev, err 1213 } 1214 1215 if !link.Prev().Eq(prev) { 1216 return nil, nilPrev, NewPrevError("team replay failed: prev chain broken at link %d (%v != %v)", 1217 i, link.Prev(), prev) 1218 } 1219 1220 if err := consumeRatchets(mctx, hiddenPackage, link); err != nil { 1221 return nil, nilPrev, err 1222 } 1223 1224 if err := checkPTKGenerationNotOnHiddenChain(mctx, hiddenPackage, link); err != nil { 1225 return nil, nilPrev, err 1226 } 1227 1228 var signer *SignerX 1229 var err error 1230 signer, err = l.verifyLink(ctx, arg.teamID, ret, arg.me, link, fullVerifyCutoff, 1231 readSubteamID, proofSet, lkc, *parentsCache) 1232 if err != nil { 1233 return nil, nilPrev, err 1234 } 1235 1236 if l.isParentChildOperation(ctx, link) { 1237 pco, err := l.toParentChildOperation(ctx, link) 1238 if err != nil { 1239 return nil, nilPrev, err 1240 } 1241 *parentChildOperations = append(*parentChildOperations, pco) 1242 } 1243 1244 ret, err = l.applyNewLink(ctx, ret, hiddenPackage.ChainData(), link, signer, arg.me) 1245 if err != nil { 1246 return nil, nilPrev, err 1247 } 1248 1249 return ret, link.LinkID(), nil 1250} 1251 1252// userPreload warms the upak cache with users who will probably need to be loaded to verify the chain. 1253// Uses teamEnv and may be disabled. 1254func (l *TeamLoader) userPreload(ctx context.Context, links []*ChainLinkUnpacked, fullVerifyCutoff keybase1.Seqno) (cancel func()) { 1255 ctx, cancel = context.WithCancel(ctx) 1256 if teamEnv.UserPreloadEnable { 1257 uidSet := make(map[keybase1.UID]struct{}) 1258 for _, link := range links { 1259 // fullVerify definition copied from verifyLink 1260 fullVerify := (link.LinkType() != libkb.SigchainV2TypeTeamLeave) || 1261 (link.Seqno() >= fullVerifyCutoff) || 1262 (link.source.EldestSeqno == 0) 1263 if !link.isStubbed() && fullVerify { 1264 uidSet[link.inner.Body.Key.UID] = struct{}{} 1265 } 1266 } 1267 l.G().Log.CDebugf(ctx, "TeamLoader userPreload uids: %v", len(uidSet)) 1268 if teamEnv.UserPreloadParallel { 1269 // Note this is full-parallel. Probably want pipelining if this is to be turned on by default. 1270 var wg sync.WaitGroup 1271 for uid := range uidSet { 1272 wg.Add(1) 1273 go func(uid keybase1.UID) { 1274 _, _, err := l.G().GetUPAKLoader().LoadV2( 1275 libkb.NewLoadUserArg(l.G()).WithUID(uid).WithPublicKeyOptional().WithNetContext(ctx)) 1276 if err != nil { 1277 l.G().Log.CDebugf(ctx, "error preloading uid %v", uid) 1278 } 1279 wg.Done() 1280 }(uid) 1281 } 1282 if teamEnv.UserPreloadWait { 1283 wg.Wait() 1284 } 1285 } else { 1286 for uid := range uidSet { 1287 _, _, err := l.G().GetUPAKLoader().LoadV2( 1288 libkb.NewLoadUserArg(l.G()).WithUID(uid).WithPublicKeyOptional().WithNetContext(ctx)) 1289 if err != nil { 1290 l.G().Log.CDebugf(ctx, "error preloading uid %v", uid) 1291 } 1292 } 1293 } 1294 } 1295 return cancel 1296} 1297 1298// Decide whether to repoll merkle based on load arg. 1299// Returns (discardCache, repoll) 1300// discardCache - the caller should throw out their cached copy and repoll. 1301// repoll - hit up merkle for the latest tail 1302// Considers: 1303// - NeedAdmin 1304// - NeedKeyGeneration 1305// - NeedApplicationsAtGenerations 1306// - WantMembers 1307// - ForceRepoll 1308// - Cache freshness / StaleOK 1309// - NeedSeqnos 1310// - JustUpdated 1311// - If this user is in global "force repoll" mode, where it would be too spammy to 1312// push out individual team changed notifications, so all team loads need a repoll. 1313func (l *TeamLoader) load2DecideRepoll(mctx libkb.MetaContext, arg load2ArgT, fromCache Teamer, cachedPolledAt *keybase1.Time) (discardCache bool, repoll bool) { 1314 var reason string 1315 defer func() { 1316 if discardCache || repoll || reason != "" { 1317 mctx.Debug("load2DecideRepoll -> (discardCache:%v, repoll:%v) %v", discardCache, repoll, reason) 1318 } 1319 }() 1320 // NeedAdmin is a special constraint where we start from scratch. 1321 // Because of admin-only invite links. 1322 if arg.needAdmin { 1323 if !l.satisfiesNeedAdmin(mctx, arg.me, fromCache) { 1324 // Start from scratch if we are newly admin 1325 reason = "!satisfiesNeedAdmin" 1326 return true, true 1327 } 1328 } 1329 1330 if arg.forceRepoll { 1331 reason = "forceRepoll" 1332 return false, true 1333 } 1334 1335 // Repoll if the server has previously hinted that the team has new links. 1336 if fromCache != nil && fromCache.MainChain() != nil && fromCache.MainChain().Chain.LastSeqno < fromCache.MainChain().LatestSeqnoHint { 1337 reason = "behind seqno hint" 1338 return false, true 1339 } 1340 1341 if fromCache != nil && fromCache.HiddenChain() != nil && fromCache.HiddenChain().IsStale() { 1342 reason = "behind hidden seqno hint" 1343 return false, true 1344 } 1345 1346 // Repoll to get a new key generation 1347 if arg.needKeyGeneration > 0 { 1348 if err := l.satisfiesNeedKeyGeneration(mctx, arg.needKeyGeneration, fromCache); err != nil { 1349 reason = fmt.Sprintf("satisfiesNeedKeyGeneration -> %v", err) 1350 return false, true 1351 } 1352 } 1353 // Repoll to get new applications at generations 1354 if len(arg.needApplicationsAtGenerations) > 0 { 1355 if err := l.satisfiesNeedApplicationsAtGenerations(mctx, arg.needApplicationsAtGenerations, fromCache); err != nil { 1356 reason = fmt.Sprintf("satisfiesNeedApplicationsAtGenerations -> %v", err) 1357 return false, true 1358 } 1359 } 1360 if arg.needKBFSKeyGeneration.Generation > 0 { 1361 if err := l.satisfiesNeedsKBFSKeyGeneration(mctx, arg.needKBFSKeyGeneration, fromCache); err != nil { 1362 reason = fmt.Sprintf("satisfiesNeedsKBFSKeyGeneration -> %v", err) 1363 return false, true 1364 } 1365 } 1366 1367 if len(arg.needApplicationsAtGenerationsWithKBFS) > 0 { 1368 if err := l.satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx, 1369 arg.needApplicationsAtGenerationsWithKBFS, fromCache); err != nil { 1370 reason = fmt.Sprintf("satisfiesNeedApplicationsAtGenerationsWithKBFS -> %v", err) 1371 return false, true 1372 } 1373 } 1374 1375 // Repoll because it might help get the wanted members 1376 if len(arg.wantMembers) > 0 { 1377 if err := l.satisfiesWantMembers(mctx, arg.wantMembers, arg.wantMembersRole, fromCache); err != nil { 1378 reason = fmt.Sprintf("satisfiesWantMembers -> %v", err) 1379 return false, true 1380 } 1381 } 1382 1383 // Repoll if we need a seqno not in the cache. 1384 // Does not force a repoll if we just need to fill in previous links 1385 if len(arg.needSeqnos) > 0 { 1386 if fromCache == nil || fromCache.MainChain() == nil { 1387 reason = "need seqnos and no cache" 1388 return false, true 1389 } 1390 if fromCache.MainChain().Chain.LastSeqno < l.seqnosMax(arg.needSeqnos) { 1391 reason = "need seqnos" 1392 return false, true 1393 } 1394 } 1395 1396 if fromCache == nil || fromCache.MainChain() == nil { 1397 reason = "no cache" 1398 // We need a merkle leaf when starting from scratch. 1399 return false, true 1400 } 1401 1402 cachedAt := fromCache.MainChain().CachedAt 1403 if cachedPolledAt != nil && *cachedPolledAt > cachedAt { 1404 cachedAt = *cachedPolledAt 1405 } 1406 1407 cacheIsOld := !l.isFresh(mctx, cachedAt) 1408 if cacheIsOld && !arg.staleOK { 1409 // We need a merkle leaf 1410 reason = "cacheIsOld" 1411 return false, true 1412 } 1413 1414 // InForceRepoll needs to a acquire a lock, so avoid it by checking it last. 1415 if l.InForceRepollMode(mctx) { 1416 reason = "InForceRepollMode" 1417 return false, true 1418 } 1419 1420 return false, false 1421} 1422 1423// Check whether the load produced a snapshot that can be returned to the caller. 1424// This should not check anything that is critical to the validity of the snapshot 1425// because the snapshot is put into the cache before this check. 1426// Considers: 1427// - NeedAdmin 1428// - NeedKeyGeneration 1429// - NeedSeqnos 1430func (l *TeamLoader) load2CheckReturn(mctx libkb.MetaContext, arg load2ArgT, shim Teamer) error { 1431 if arg.needAdmin { 1432 if !l.satisfiesNeedAdmin(mctx, arg.me, shim) { 1433 mctx.Debug("user %v is not an admin of team %v at seqno:%v", arg.me, arg.teamID, shim.MainChain().Chain.LastSeqno) 1434 return fmt.Errorf("user %v is not an admin of the team", arg.me) 1435 } 1436 } 1437 1438 // Repoll to get a new key generation 1439 if arg.needKeyGeneration > 0 { 1440 if err := l.satisfiesNeedKeyGeneration(mctx, arg.needKeyGeneration, shim); err != nil { 1441 return err 1442 } 1443 } 1444 if len(arg.needApplicationsAtGenerations) > 0 { 1445 if err := l.satisfiesNeedApplicationsAtGenerations(mctx, arg.needApplicationsAtGenerations, shim); err != nil { 1446 return err 1447 } 1448 } 1449 if arg.needKBFSKeyGeneration.Generation > 0 { 1450 if err := l.satisfiesNeedsKBFSKeyGeneration(mctx, arg.needKBFSKeyGeneration, shim); err != nil { 1451 return err 1452 } 1453 } 1454 if len(arg.needApplicationsAtGenerationsWithKBFS) > 0 { 1455 if err := l.satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx, arg.needApplicationsAtGenerationsWithKBFS, shim); err != nil { 1456 return err 1457 } 1458 } 1459 1460 if len(arg.needSeqnos) > 0 { 1461 if err := l.checkNeededSeqnos(mctx.Ctx(), shim.MainChain(), arg.needSeqnos); err != nil { 1462 return err 1463 } 1464 } 1465 1466 return nil 1467} 1468 1469// Whether the user is an admin at the snapshot, and there are no stubbed links, and keys are up to date. 1470func (l *TeamLoader) satisfiesNeedAdmin(mctx libkb.MetaContext, me keybase1.UserVersion, team Teamer) bool { 1471 if team == nil || team.MainChain() == nil { 1472 return false 1473 } 1474 state := newTeamSigChainState(team) 1475 if state.HasAnyStubbedLinks() { 1476 return false 1477 } 1478 if !l.hasSyncedSecrets(mctx, team) { 1479 return false 1480 } 1481 role, err := state.GetUserRole(me) 1482 if err != nil { 1483 mctx.Debug("TeamLoader error getting my role: %v", err) 1484 return false 1485 } 1486 if !role.IsAdminOrAbove() { 1487 if !state.IsSubteam() { 1488 return false 1489 } 1490 yes, err := l.isImplicitAdminOf(mctx.Ctx(), state.GetID(), state.GetParentID(), me, me) 1491 if err != nil { 1492 mctx.Debug("TeamLoader error getting checking implicit admin: %s", err) 1493 return false 1494 } 1495 if !yes { 1496 return false 1497 } 1498 } 1499 return true 1500} 1501 1502// Check whether a user is an implicit admin of a team. 1503func (l *TeamLoader) isImplicitAdminOf(ctx context.Context, teamID keybase1.TeamID, ancestorID *keybase1.TeamID, 1504 me keybase1.UserVersion, uv keybase1.UserVersion) (bool, error) { 1505 1506 // IDs of ancestors that were not freshly polled. 1507 // Check them again with forceRepoll if the affirmative is not found cached. 1508 checkAgain := make(map[keybase1.TeamID]bool) 1509 1510 check1 := func(chain *TeamSigChainState) bool { 1511 role, err := chain.GetUserRole(uv) 1512 if err != nil { 1513 return false 1514 } 1515 return role.IsAdminOrAbove() 1516 } 1517 1518 i := 0 1519 for { 1520 i++ 1521 if i >= 100 { 1522 // Break in case there's a bug in this loop. 1523 return false, fmt.Errorf("stuck in a loop while checking for implicit admin: %v", ancestorID) 1524 } 1525 1526 // Use load2 so that we can use subteam-reader and get secretless teams. 1527 ancestor, err := l.load2(ctx, load2ArgT{ 1528 teamID: *ancestorID, 1529 reason: "isImplicitAdminOf-1", 1530 me: me, 1531 readSubteamID: &teamID, 1532 }) 1533 if err != nil { 1534 return false, err 1535 } 1536 // Be wary, `ancestor` could be, and is likely, a secretless team. 1537 // Do not let it out of sight. 1538 ancestorChain := TeamSigChainState{inner: ancestor.team.Chain} 1539 1540 if !ancestor.didRepoll { 1541 checkAgain[ancestorChain.GetID()] = true 1542 } 1543 1544 if check1(&ancestorChain) { 1545 return true, nil 1546 } 1547 1548 if !ancestorChain.IsSubteam() { 1549 break 1550 } 1551 // Get the next level up. 1552 ancestorID = ancestorChain.GetParentID() 1553 } 1554 1555 // The answer was not found to be yes in the cache. 1556 // Try again with the teams that were not polled as they might have unseen updates. 1557 for ancestorID := range checkAgain { 1558 ancestor, err := l.load2(ctx, load2ArgT{ 1559 teamID: ancestorID, 1560 reason: "isImplicitAdminOf-again", 1561 me: me, 1562 forceRepoll: true, // Get the latest info. 1563 readSubteamID: &teamID, 1564 }) 1565 if err != nil { 1566 return false, err 1567 } 1568 // Be wary, `ancestor` could be, and is likely, a secretless team. 1569 // Do not let it out of sight. 1570 ancestorChain := TeamSigChainState{inner: ancestor.team.Chain} 1571 if check1(&ancestorChain) { 1572 return true, nil 1573 } 1574 } 1575 1576 return false, nil 1577} 1578 1579func (l *TeamLoader) satisfiesNeedsKBFSKeyGeneration(mctx libkb.MetaContext, 1580 kbfs keybase1.TeamKBFSKeyRefresher, state Teamer) error { 1581 if kbfs.Generation == 0 { 1582 return nil 1583 } 1584 if state == nil { 1585 return fmt.Errorf("nil team does not contain KBFS key generation: %#v", kbfs) 1586 } 1587 1588 gen, err := newTeamSigChainState(state).GetLatestKBFSGeneration(kbfs.AppType) 1589 if err != nil { 1590 return err 1591 } 1592 if kbfs.Generation > gen { 1593 return NewKBFSKeyGenerationError(kbfs.Generation, gen) 1594 } 1595 return nil 1596} 1597 1598// Whether the snapshot has loaded at least up to the key generation and has the secret. 1599func (l *TeamLoader) satisfiesNeedKeyGeneration(mctx libkb.MetaContext, needKeyGeneration keybase1.PerTeamKeyGeneration, state Teamer) error { 1600 if needKeyGeneration == 0 { 1601 return nil 1602 } 1603 if state == nil { 1604 return fmt.Errorf("nil team does not contain key generation: %v", needKeyGeneration) 1605 } 1606 key, err := newTeamSigChainState(state).GetLatestPerTeamKey(mctx) 1607 if err != nil { 1608 return err 1609 } 1610 if needKeyGeneration > key.Gen { 1611 return fmt.Errorf("team key generation too low: %v < %v", key.Gen, needKeyGeneration) 1612 } 1613 _, ok := state.MainChain().PerTeamKeySeedsUnverified[needKeyGeneration] 1614 if !ok { 1615 return fmt.Errorf("team key secret missing for generation: %v", needKeyGeneration) 1616 } 1617 return nil 1618} 1619 1620// Whether the snapshot has loaded the reader key masks and key generations we 1621// need. 1622func (l *TeamLoader) satisfiesNeedApplicationsAtGenerations(mctx libkb.MetaContext, 1623 needApplicationsAtGenerations map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication, team Teamer) error { 1624 if len(needApplicationsAtGenerations) == 0 { 1625 return nil 1626 } 1627 if team == nil || team.MainChain() == nil { 1628 return fmt.Errorf("nil team does not contain applications: %v", needApplicationsAtGenerations) 1629 } 1630 for ptkGen, apps := range needApplicationsAtGenerations { 1631 for _, app := range apps { 1632 if _, err := ApplicationKeyAtGeneration(mctx, team, app, ptkGen); err != nil { 1633 return err 1634 } 1635 } 1636 } 1637 return nil 1638} 1639 1640func (l *TeamLoader) satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx libkb.MetaContext, 1641 needApplicationsAtGenerations map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication, 1642 state Teamer) error { 1643 if len(needApplicationsAtGenerations) == 0 { 1644 return nil 1645 } 1646 if state == nil || state.MainChain() == nil { 1647 return fmt.Errorf("nil team does not contain applications: %v", needApplicationsAtGenerations) 1648 } 1649 for ptkGen, apps := range needApplicationsAtGenerations { 1650 for _, app := range apps { 1651 if _, err := ApplicationKeyAtGenerationWithKBFS(mctx, state, app, ptkGen); err != nil { 1652 return err 1653 } 1654 } 1655 } 1656 return nil 1657} 1658 1659// Whether the snapshot has each of `wantMembers` as a member. 1660func (l *TeamLoader) satisfiesWantMembers(mctx libkb.MetaContext, 1661 wantMembers []keybase1.UserVersion, wantMembersRole keybase1.TeamRole, state Teamer) error { 1662 1663 if wantMembersRole == keybase1.TeamRole_NONE { 1664 // Default to writer. 1665 wantMembersRole = keybase1.TeamRole_WRITER 1666 } 1667 if len(wantMembers) == 0 { 1668 return nil 1669 } 1670 if state == nil { 1671 return fmt.Errorf("nil team does not have wanted members") 1672 } 1673 for _, uv := range wantMembers { 1674 role, err := newTeamSigChainState(state).GetUserRole(uv) 1675 if err != nil { 1676 return fmt.Errorf("could not get wanted user role: %v", err) 1677 } 1678 if !role.IsOrAbove(wantMembersRole) { 1679 return fmt.Errorf("wanted user %v is a %v which is not at least %v", uv, role, wantMembersRole) 1680 } 1681 } 1682 return nil 1683} 1684 1685func (l *TeamLoader) mungeWantMembers(ctx context.Context, wantMembers []keybase1.UserVersion) (res []keybase1.UserVersion, err error) { 1686 for _, uv1 := range wantMembers { 1687 uv2 := uv1 1688 if uv2.EldestSeqno == 0 { 1689 // Lookup the latest eldest seqno for that uid. 1690 // This value may come from a cache. 1691 uv2.EldestSeqno, err = l.world.lookupEldestSeqno(ctx, uv2.Uid) 1692 if err != nil { 1693 return res, err 1694 } 1695 l.G().Log.CDebugf(ctx, "TeamLoader resolved wantMember %v -> %v", uv2.Uid, uv2.EldestSeqno) 1696 } 1697 res = append(res, uv2) 1698 } 1699 return res, err 1700} 1701 1702// Whether y is in xs. 1703func (l *TeamLoader) seqnosContains(xs []keybase1.Seqno, y keybase1.Seqno) bool { 1704 for _, x := range xs { 1705 if x.Eq(y) { 1706 return true 1707 } 1708 } 1709 return false 1710} 1711 1712// Return the max in a list of positive seqnos. Returns 0 if the list is empty 1713func (l *TeamLoader) seqnosMax(seqnos []keybase1.Seqno) (ret keybase1.Seqno) { 1714 for _, x := range seqnos { 1715 if x > ret { 1716 ret = x 1717 } 1718 } 1719 return ret 1720} 1721 1722// Whether a TeamData from the cache is fresh. 1723func (l *TeamLoader) isFresh(mctx libkb.MetaContext, cachedAt keybase1.Time) bool { 1724 if cachedAt.IsZero() { 1725 // This should never happen. 1726 mctx.Warning("TeamLoader encountered zero cached time") 1727 return false 1728 } 1729 diff := mctx.G().Clock().Now().Sub(cachedAt.Time()) 1730 fresh := (diff <= freshnessLimit) 1731 if !fresh { 1732 mctx.Debug("TeamLoader cached snapshot is old: %v", diff) 1733 } 1734 return fresh 1735} 1736 1737// Whether the teams secrets are synced to the same point as its sigchain 1738// Does not check RKMs. 1739func (l *TeamLoader) hasSyncedSecrets(mctx libkb.MetaContext, team Teamer) bool { 1740 state := team.MainChain() 1741 n := len(team.MainChain().Chain.PerTeamKeys) 1742 offChainGen := len(state.PerTeamKeySeedsUnverified) 1743 mctx.Debug("TeamLoader#hasSyncedSecrets: found %d PTKs on the main chain (versus %d seeds)", n, offChainGen) 1744 if team.HiddenChain() != nil { 1745 m := len(team.HiddenChain().ReaderPerTeamKeys) 1746 mctx.Debug("TeamLoader#hasSyncedSecrets: found another %d PTKs on the hidden chain", m) 1747 n += m 1748 } 1749 return (n == offChainGen) 1750} 1751 1752func (l *TeamLoader) logIfUnsyncedSecrets(ctx context.Context, state *keybase1.TeamData) { 1753 onChainGen := keybase1.PerTeamKeyGeneration(len(state.Chain.PerTeamKeys)) 1754 offChainGen := keybase1.PerTeamKeyGeneration(len(state.PerTeamKeySeedsUnverified)) 1755 if onChainGen != offChainGen { 1756 l.G().Log.CDebugf(ctx, "TeamLoader unsynced secrets local:%v != chain:%v ", offChainGen, onChainGen) 1757 } 1758} 1759 1760func (l *TeamLoader) lows(mctx libkb.MetaContext, state *keybase1.TeamData, hp *hidden.LoaderPackage, arg load2ArgT) getLinksLows { 1761 var lows getLinksLows 1762 if state != nil { 1763 chain := TeamSigChainState{inner: state.Chain} 1764 lows.Seqno = chain.GetLatestSeqno() 1765 if !arg.foundRKMHole { 1766 lows.PerTeamKey = keybase1.PerTeamKeyGeneration(len(state.PerTeamKeySeedsUnverified)) 1767 } 1768 } 1769 if hp != nil { 1770 lows.HiddenChainSeqno = hp.LastFullSeqno() 1771 } 1772 return lows 1773} 1774 1775func (l *TeamLoader) OnLogout(mctx libkb.MetaContext) error { 1776 l.storage.ClearMem() 1777 return nil 1778} 1779 1780func (l *TeamLoader) OnDbNuke(mctx libkb.MetaContext) error { 1781 l.storage.ClearMem() 1782 return nil 1783} 1784 1785// Clear the in-memory cache. 1786func (l *TeamLoader) ClearMem() { 1787 l.storage.ClearMem() 1788} 1789 1790func (l *TeamLoader) VerifyTeamName(ctx context.Context, id keybase1.TeamID, name keybase1.TeamName) error { 1791 if name.IsRootTeam() { 1792 if !name.ToTeamID(id.IsPublic()).Eq(id) { 1793 return NewResolveError(name, id) 1794 } 1795 return nil 1796 } 1797 teamData, _, err := l.Load(ctx, keybase1.LoadTeamArg{ 1798 ID: id, 1799 Public: id.IsPublic(), 1800 }) 1801 if err != nil { 1802 return err 1803 } 1804 gotName := teamData.Name 1805 if !gotName.Eq(name) { 1806 return NewResolveError(name, id) 1807 } 1808 return nil 1809} 1810 1811// List all the admins of ancestor teams. 1812// Includes admins of the specified team only if they are also admins of ancestor teams. 1813// The specified team must be a subteam, or an error is returned. 1814// Always sends a flurry of RPCs to get the most up to date info. 1815func (l *TeamLoader) ImplicitAdmins(ctx context.Context, teamID keybase1.TeamID) (impAdmins []keybase1.UserVersion, err error) { 1816 impAdminsMap := make(map[string]keybase1.UserVersion) // map to remove dups 1817 err = l.MapTeamAncestors(ctx, func(t keybase1.TeamSigChainState, _ keybase1.TeamName) error { 1818 ancestorChain := TeamSigChainState{inner: t} 1819 // Gather the admins. 1820 adminRoles := []keybase1.TeamRole{keybase1.TeamRole_OWNER, keybase1.TeamRole_ADMIN} 1821 for _, role := range adminRoles { 1822 uvs, err := ancestorChain.GetUsersWithRole(role) 1823 if err != nil { 1824 return err 1825 } 1826 for _, uv := range uvs { 1827 impAdminsMap[uv.String()] = uv 1828 } 1829 } 1830 return nil 1831 }, teamID, "implicitAdminsAncestor", func(keybase1.TeamSigChainState) bool { return true }) 1832 if err != nil { 1833 return nil, err 1834 } 1835 for _, uv := range impAdminsMap { 1836 impAdmins = append(impAdmins, uv) 1837 } 1838 return impAdmins, nil 1839} 1840 1841// MapTeamAncestors does NOT map over the team itself. 1842func (l *TeamLoader) MapTeamAncestors( 1843 ctx context.Context, 1844 f func(keybase1.TeamSigChainState, keybase1.TeamName) error, 1845 teamID keybase1.TeamID, 1846 reason string, 1847 forceFullReloadOnceToAssert func(t keybase1.TeamSigChainState) bool, 1848) (err error) { 1849 initialTeamIdx := 0 1850 1851 me, err := l.world.getMe(ctx) 1852 if err != nil { 1853 return NewMapAncestorsError(err, initialTeamIdx) 1854 } 1855 1856 // Load the argument team 1857 team, _, err := l.load1(ctx, me, keybase1.LoadTeamArg{ 1858 ID: teamID, 1859 Public: teamID.IsPublic(), 1860 StaleOK: true, // We only use immutable fields. 1861 }) 1862 if err != nil { 1863 return NewMapAncestorsError(err, initialTeamIdx) 1864 } 1865 teamChain := TeamSigChainState{inner: team.Chain} 1866 if !teamChain.IsSubteam() { 1867 return NewMapAncestorsError( 1868 fmt.Errorf("cannot map over parents of a root team: %v", teamID), 1869 initialTeamIdx, 1870 ) 1871 } 1872 return l.mapTeamAncestorsHelper(ctx, f, teamID, teamChain.GetParentID(), reason, forceFullReloadOnceToAssert) 1873} 1874 1875func (l *TeamLoader) mapTeamAncestorsHelper( 1876 ctx context.Context, 1877 f func(keybase1.TeamSigChainState, keybase1.TeamName) error, 1878 teamID keybase1.TeamID, 1879 ancestorID *keybase1.TeamID, 1880 reason string, 1881 forceFullReloadOnceToAssert func(t keybase1.TeamSigChainState) bool, 1882) (err error) { 1883 i := 0 1884 1885 defer func() { 1886 if err != nil { 1887 err = NewMapAncestorsError(err, i) 1888 } 1889 }() 1890 1891 me, err := l.world.getMe(ctx) 1892 if err != nil { 1893 return err 1894 } 1895 1896 for { 1897 i++ 1898 if i >= 100 { 1899 // Break in case there's a bug in this loop. 1900 return fmt.Errorf("stuck in a loop while mapping over team parents: %v", ancestorID) 1901 } 1902 1903 load2Arg := load2ArgT{ 1904 teamID: *ancestorID, 1905 reason: reason, 1906 me: me, 1907 forceRepoll: true, // Get the latest info. 1908 readSubteamID: &teamID, 1909 } 1910 1911 var ancestor *load2ResT 1912 for { 1913 var err error 1914 // Use load2 so that we can use subteam-reader and get secretless teams. 1915 ancestor, err = l.load2(ctx, load2Arg) 1916 if err != nil { 1917 return err 1918 } 1919 1920 if forceFullReloadOnceToAssert == nil || 1921 forceFullReloadOnceToAssert(ancestor.team.Chain) { 1922 break 1923 } 1924 if load2Arg.forceFullReload { 1925 return fmt.Errorf("failed to assert predicate in ancestor %v after full force reload", ancestor.team.ID()) 1926 } 1927 load2Arg.forceFullReload = true 1928 } 1929 1930 // Be wary, `ancestor` could be, and is likely, a secretless team. 1931 // Do not let it out of sight. 1932 ancestorChain := TeamSigChainState{inner: ancestor.team.Chain} 1933 1934 err = f(ancestor.team.Chain, ancestor.team.Name) 1935 if err != nil { 1936 return err 1937 } 1938 1939 if !ancestorChain.IsSubteam() { 1940 break 1941 } 1942 // Get the next level up. 1943 ancestorID = ancestorChain.GetParentID() 1944 } 1945 1946 return nil 1947} 1948 1949func (l *TeamLoader) NotifyTeamRename(ctx context.Context, id keybase1.TeamID, newName string) error { 1950 // ignore newName from the server 1951 1952 // Load up the ancestor chain with ForceRepoll. 1953 // Then load down the ancestor chain without it (expect cache hits). 1954 // Not the most elegant way, but it will get the job done. 1955 // Each load on the way down will recalculate that team's name. 1956 1957 var ancestorIDs []keybase1.TeamID 1958 1959 me, err := l.world.getMe(ctx) 1960 if err != nil { 1961 return err 1962 } 1963 1964 loopID := &id 1965 for loopID != nil { 1966 load2Res, err := l.load2(ctx, load2ArgT{ 1967 teamID: *loopID, 1968 reason: "NotifyTeamRename-force", 1969 forceRepoll: true, 1970 readSubteamID: &id, 1971 me: me, 1972 }) 1973 if err != nil { 1974 return err 1975 } 1976 ancestorIDs = append(ancestorIDs, *loopID) 1977 chain := TeamSigChainState{inner: load2Res.team.Chain} 1978 if chain.IsSubteam() { 1979 loopID = chain.GetParentID() 1980 } else { 1981 loopID = nil 1982 } 1983 } 1984 1985 // reverse ancestorIDs so the root team appears first 1986 sort.SliceStable(ancestorIDs, func(i, j int) bool { return i > j }) 1987 1988 for _, loopID := range ancestorIDs { 1989 _, err := l.load2(ctx, load2ArgT{ 1990 teamID: loopID, 1991 reason: "NotifyTeamRename-quick", 1992 readSubteamID: &id, 1993 me: me, 1994 }) 1995 if err != nil { 1996 return err 1997 } 1998 } 1999 2000 return nil 2001} 2002 2003func (l *TeamLoader) getHeadMerkleSeqno(mctx libkb.MetaContext, readSubteamID keybase1.TeamID, state *keybase1.TeamSigChainState) (ret keybase1.Seqno, err error) { 2004 defer mctx.Trace("TeamLoader#getHeadMerkleSeqno", &err)() 2005 2006 if state.HeadMerkle != nil { 2007 return state.HeadMerkle.Seqno, nil 2008 } 2009 headSeqno := keybase1.Seqno(1) 2010 expectedLinkRaw, ok := state.LinkIDs[headSeqno] 2011 if !ok { 2012 return ret, fmt.Errorf("couldn't find head link in team state during audit") 2013 } 2014 expectedLink, err := libkb.ImportLinkID(expectedLinkRaw) 2015 if err != nil { 2016 return ret, err 2017 } 2018 teamUpdate, err := l.world.getLinksFromServer(mctx.Ctx(), state.Id, []keybase1.Seqno{headSeqno}, &readSubteamID) 2019 if err != nil { 2020 return ret, err 2021 } 2022 newLinks, err := teamUpdate.unpackLinks(mctx) 2023 if err != nil { 2024 return ret, err 2025 } 2026 if len(newLinks) != 1 { 2027 return ret, fmt.Errorf("expected only one chainlink back; got %d", len(newLinks)) 2028 } 2029 headLink := newLinks[0] 2030 err = headLink.AssertInnerOuterMatch() 2031 if err != nil { 2032 return ret, err 2033 } 2034 if headLink.Seqno() != headSeqno { 2035 return ret, NewInvalidLink(headLink, "wrong head seqno; wanted 1 but got something else") 2036 } 2037 if !headLink.LinkID().Eq(expectedLink) { 2038 return ret, NewInvalidLink(headLink, "wrong head link hash: %s != %s", headLink.LinkID(), expectedLink) 2039 } 2040 if headLink.isStubbed() { 2041 return ret, NewInvalidLink(headLink, "got a stubbed head link, but wasn't expecting that") 2042 } 2043 headMerkle := headLink.inner.Body.MerkleRoot.ToMerkleRootV2() 2044 state.HeadMerkle = &headMerkle 2045 return headMerkle.Seqno, nil 2046} 2047 2048func (l *TeamLoader) audit(ctx context.Context, readSubteamID keybase1.TeamID, state *keybase1.TeamSigChainState, hiddenChain *keybase1.HiddenTeamChain, lastMerkleRoot *libkb.MerkleRoot, auditMode keybase1.AuditMode) (err error) { 2049 mctx := libkb.NewMetaContext(ctx, l.G()) 2050 2051 if l.G().Env.Test.TeamSkipAudit { 2052 mctx.Debug("skipping audit in test due to flag") 2053 return nil 2054 } 2055 2056 headMerklSeqno, err := l.getHeadMerkleSeqno(mctx, readSubteamID, state) 2057 if err != nil { 2058 return err 2059 } 2060 2061 err = mctx.G().GetTeamAuditor().AuditTeam(mctx, state.Id, state.Public, headMerklSeqno, state.LinkIDs, hiddenChain.GetOuter(), state.LastSeqno, hiddenChain.GetLastCommittedSeqno(), lastMerkleRoot, auditMode) 2062 return err 2063} 2064 2065func (l *TeamLoader) ForceRepollUntil(ctx context.Context, dtime gregor.TimeOrOffset) error { 2066 l.G().Log.CDebugf(ctx, "TeamLoader#ForceRepollUntil(%+v)", dtime) 2067 l.forceRepollMutex.Lock() 2068 defer l.forceRepollMutex.Unlock() 2069 l.forceRepollUntil = dtime 2070 return nil 2071} 2072 2073func (l *TeamLoader) InForceRepollMode(mctx libkb.MetaContext) bool { 2074 l.forceRepollMutex.Lock() 2075 defer l.forceRepollMutex.Unlock() 2076 if l.forceRepollUntil == nil { 2077 return false 2078 } 2079 if !l.forceRepollUntil.Before(mctx.G().Clock().Now()) { 2080 mctx.Debug("TeamLoader#InForceRepollMode: returning true") 2081 return true 2082 } 2083 l.forceRepollUntil = nil 2084 return false 2085} 2086