1package libkb
2
3import (
4	"fmt"
5	"runtime/debug"
6	"time"
7
8	logger "github.com/keybase/client/go/logger"
9	"github.com/keybase/client/go/profiling"
10	keybase1 "github.com/keybase/client/go/protocol/keybase1"
11	context "golang.org/x/net/context"
12)
13
14type APITokener interface {
15	Tokens() (session, csrf string)
16}
17
18type MetaContext struct {
19	ctx          context.Context
20	g            *GlobalContext
21	loginContext LoginContext
22	activeDevice *ActiveDevice
23	apiTokener   APITokener
24	uis          UIs
25}
26
27func (m MetaContext) Dump() {
28	m.Debug("MetaContext#Dump:")
29	if m.activeDevice != nil {
30		m.Debug("- Local ActiveDevice:")
31		m.activeDevice.Dump(m, "-- ")
32	}
33	m.Debug("- Global ActiveDevice:")
34	m.g.ActiveDevice.Dump(m, "-- ")
35	if m.loginContext != nil {
36		m.Debug("- Login Context:")
37		m.loginContext.Dump(m, "-- ")
38	}
39}
40
41func NewMetaContext(ctx context.Context, g *GlobalContext) MetaContext {
42	return MetaContext{ctx: ctx, g: g}
43}
44
45func (m MetaContext) WithLoginContext(l LoginContext) MetaContext {
46	m.loginContext = l
47	return m
48}
49
50func (m MetaContext) WithAPITokener(t APITokener) MetaContext {
51	m.apiTokener = t
52	return m
53}
54
55func (m MetaContext) WithCtx(c context.Context) MetaContext {
56	m.ctx = c
57	return m
58}
59
60func (m MetaContext) G() *GlobalContext {
61	return m.g
62}
63
64func (m MetaContext) Ctx() context.Context {
65	return m.ctx
66}
67
68func (m MetaContext) LoginContext() LoginContext {
69	return m.loginContext
70}
71
72func (m MetaContext) VLogf(lev VDebugLevel, msg string, args ...interface{}) {
73	m.g.VDL.CLogfWithAddedDepth(m.ctx, lev, 1, msg, args...)
74}
75
76func (m MetaContext) Trace(msg string, err *error) func() {
77	return CTrace(m.ctx, m.g.Log.CloneWithAddedDepth(1), msg, err, m.G().Clock())
78}
79func (m MetaContext) VTrace(lev VDebugLevel, msg string, err *error) func() {
80	return m.g.CVTrace(m.ctx, lev, msg, err)
81}
82func (m MetaContext) PerfTrace(msg string, err *error) func() {
83	return CTrace(m.ctx, m.g.PerfLog.CloneWithAddedDepth(1), msg, err, m.G().Clock())
84}
85func (m MetaContext) TimeBuckets() (MetaContext, *profiling.TimeBuckets) {
86	var ret *profiling.TimeBuckets
87	m.ctx, ret = m.G().CTimeBuckets(m.ctx)
88	return m, ret
89}
90func (m MetaContext) TimeTracer(label string, enabled bool) profiling.TimeTracer {
91	return m.G().CTimeTracer(m.Ctx(), label, enabled)
92}
93
94func (m MetaContext) Debug(f string, args ...interface{}) {
95	m.g.Log.CloneWithAddedDepth(1).CDebugf(m.ctx, f, args...)
96}
97func (m MetaContext) PerfDebug(f string, args ...interface{}) {
98	m.g.PerfLog.CloneWithAddedDepth(1).CDebugf(m.ctx, f, args...)
99}
100func (m MetaContext) Warning(f string, args ...interface{}) {
101	m.g.Log.CloneWithAddedDepth(1).CWarningf(m.ctx, f, args...)
102}
103func (m MetaContext) Error(f string, args ...interface{}) {
104	m.g.Log.CloneWithAddedDepth(1).CErrorf(m.ctx, f, args...)
105}
106func (m MetaContext) Info(f string, args ...interface{}) {
107	m.g.Log.CloneWithAddedDepth(1).CInfof(m.ctx, f, args...)
108}
109
110func (m MetaContext) ActiveDevice() *ActiveDevice {
111	if m.activeDevice != nil {
112		m.Debug("MetaContext#ActiveDevice: thread local")
113		return m.activeDevice
114	}
115	m.Debug("MetaContext#ActiveDevice: global")
116	return m.G().ActiveDevice
117}
118
119func (m MetaContext) NIST() (*NIST, error) {
120	nist, uid, _, err := m.ActiveDevice().NISTAndUIDDeviceID(m.Ctx())
121	if err != nil {
122		return nil, err
123	}
124	if !uid.Equal(m.CurrentUID()) {
125		m.Debug("MetaContext#NIST: Not returning nist, since for wrong UID: %s != %s", uid, m.CurrentUID())
126		return nil, nil
127	}
128	return nist, nil
129}
130
131func NewMetaContextTODO(g *GlobalContext) MetaContext {
132	return MetaContext{ctx: context.TODO(), g: g}
133}
134func NewMetaContextBackground(g *GlobalContext) MetaContext {
135	return MetaContext{ctx: context.Background(), g: g}
136}
137
138func (m MetaContext) WithDelegatedIdentifyUI(u IdentifyUI) MetaContext {
139	m.uis.IdentifyUI = u
140	return m
141}
142
143func (m MetaContext) WithContext(ctx context.Context) MetaContext {
144	m.ctx = ctx
145	return m
146}
147
148func (m MetaContext) WithContextCancel() (MetaContext, func()) {
149	var f func()
150	m.ctx, f = context.WithCancel(m.ctx)
151	return m, f
152}
153
154func (m MetaContext) BackgroundWithCancel() (MetaContext, func()) {
155	var f func()
156	m.ctx, f = context.WithCancel(context.Background())
157	return m, f
158}
159
160func (m MetaContext) BackgroundWithLogTags() MetaContext {
161	m.ctx = CopyTagsToBackground(m.ctx)
162	return m
163}
164
165func (m MetaContext) WithTimeout(timeout time.Duration) (MetaContext, func()) {
166	var f func()
167	m.ctx, f = context.WithTimeout(m.ctx, timeout)
168	return m, f
169}
170
171func (m MetaContext) WithLogTag(k string) MetaContext {
172	m.ctx = WithLogTag(m.ctx, k)
173	return m
174}
175
176func (m MetaContext) WithLogTags(tags map[string]string) MetaContext {
177	for k, v := range tags {
178		m.ctx = WithLogTagWithValue(m.ctx, k, v)
179	}
180	return m
181}
182
183func (m MetaContext) WithTimeBuckets() (MetaContext, *profiling.TimeBuckets) {
184	ctx, tbs := m.G().CTimeBuckets(m.ctx)
185	m.ctx = ctx
186	return m, tbs
187}
188
189func (m MetaContext) EnsureCtx() MetaContext {
190	if m.ctx == nil {
191		m.ctx = context.Background()
192		m.Debug("installing background context.Context")
193	}
194	return m
195}
196
197func (m MetaContext) WithSecretUI(u SecretUI) MetaContext {
198	m.uis.SecretUI = u
199	return m
200}
201
202func (m MetaContext) WithLogUI(u LogUI) MetaContext {
203	m.uis.LogUI = u
204	return m
205}
206
207func (m MetaContext) WithPgpUI(u PgpUI) MetaContext {
208	m.uis.PgpUI = u
209	return m
210}
211
212func (m MetaContext) WithIdentifyUI(u IdentifyUI) MetaContext {
213	m.uis.IdentifyUI = u
214	return m
215}
216
217func (m MetaContext) WithGPGUI(u GPGUI) MetaContext {
218	m.uis.GPGUI = u
219	return m
220}
221
222func (m MetaContext) WithSaltpackUI(s SaltpackUI) MetaContext {
223	m.uis.SaltpackUI = s
224	return m
225}
226
227func (m MetaContext) UIs() UIs {
228	return m.uis
229}
230
231func (m MetaContext) WithUIs(u UIs) MetaContext {
232	m.uis = u
233	return m
234}
235
236func (m MetaContext) WithActiveDevice(a *ActiveDevice) MetaContext {
237	m.activeDevice = a
238	return m
239}
240
241func (m MetaContext) WithProvisioningKeyActiveDevice(d *DeviceWithKeys, uv keybase1.UserVersion) MetaContext {
242	return m.WithActiveDevice(d.ToProvisioningKeyActiveDevice(m, uv))
243}
244
245func (m MetaContext) WithGlobalActiveDevice() MetaContext {
246	m.activeDevice = nil
247	return m
248}
249
250func (m MetaContext) SecretKeyPromptArg(ska SecretKeyArg, reason string) SecretKeyPromptArg {
251	return SecretKeyPromptArg{
252		SecretUI: m.uis.SecretUI,
253		Ska:      ska,
254		Reason:   reason,
255	}
256}
257
258func (m MetaContext) WithNewProvisionalLoginContext() MetaContext {
259	return m.WithLoginContext(newProvisionalLoginContext(m))
260}
261
262func (m MetaContext) WithNewProvisionalLoginContextForUser(u *User) MetaContext {
263	return m.WithNewProvisionalLoginContextForUserVersionAndUsername(u.ToUserVersion(), u.GetNormalizedName())
264}
265
266func (m MetaContext) WithNewProvisionalLoginContextForUserVersionAndUsername(uv keybase1.UserVersion, un NormalizedUsername) MetaContext {
267	plc := newProvisionalLoginContextWithUserVersionAndUsername(m, uv, un)
268	err := m.ActiveDevice().CopyCacheToLoginContextIfForUserVersion(m, plc, uv)
269	if err != nil {
270		m.Debug("WithNewProvisionalLoginContextForUserVersionAndUsername: error %+v", err)
271	}
272	return m.WithLoginContext(plc)
273}
274
275func (m MetaContext) CommitProvisionalLogin() MetaContext {
276	m.Debug("MetaContext#CommitProvisionalLogin")
277	lctx := m.loginContext
278	m.loginContext = nil
279	if lctx != nil {
280		if ppsc := lctx.PassphraseStreamCache(); ppsc != nil {
281			m.ActiveDevice().CachePassphraseStream(ppsc)
282		}
283	}
284	return m
285}
286
287type UIs struct {
288	GPGUI       GPGUI
289	LogUI       LogUI
290	LoginUI     LoginUI
291	SecretUI    SecretUI
292	IdentifyUI  IdentifyUI
293	PgpUI       PgpUI
294	ProveUI     ProveUI
295	ProvisionUI ProvisionUI
296	SaltpackUI  SaltpackUI
297
298	// Usually set to `NONE`, meaning none specified.
299	// But if we know it, specify the end client type here
300	// since some things like GPG shell-out work differently
301	// depending.
302	ClientType keybase1.ClientType
303
304	SessionID int
305}
306
307func (e UIs) HasUI(kind UIKind) bool {
308	switch kind {
309	case GPGUIKind:
310		return e.GPGUI != nil
311	case LogUIKind:
312		return e.LogUI != nil
313	case LoginUIKind:
314		return e.LoginUI != nil
315	case SecretUIKind:
316		return e.SecretUI != nil
317	case IdentifyUIKind:
318		return e.IdentifyUI != nil
319	case PgpUIKind:
320		return e.PgpUI != nil
321	case ProveUIKind:
322		return e.ProveUI != nil
323	case ProvisionUIKind:
324		return e.ProvisionUI != nil
325	case SaltpackUIKind:
326		return e.SaltpackUI != nil
327	}
328	panic(fmt.Sprintf("unhandled kind: %d", kind))
329}
330
331type MetaContextified struct {
332	m MetaContext
333}
334
335func (m MetaContextified) M() MetaContext {
336	return m.m
337}
338
339func (m MetaContextified) G() *GlobalContext {
340	return m.m.g
341}
342
343func NewMetaContextified(m MetaContext) MetaContextified {
344	return MetaContextified{m: m}
345}
346
347// SwitchUserNewConfig switches the global active "user" as far as the global
348// config file is concerned.  It switches the user to a new user, and therefore
349// you should specify the username, salt, and device ID for this user on this
350// device. It will take out the global `switchUserMu` and also clear out the
351// global ActiveDevice at the same time. We follow the same pattern here and
352// elsewhere: atomically mutate the `current_user` of the config file as we set
353// the global ActiveDevice.
354func (m MetaContext) SwitchUserNewConfig(u keybase1.UID, n NormalizedUsername, salt []byte, d keybase1.DeviceID) error {
355	return m.switchUserNewConfig(u, n, salt, d, nil)
356}
357
358func (m MetaContext) switchUserNewConfig(u keybase1.UID, n NormalizedUsername, salt []byte, d keybase1.DeviceID, ad *ActiveDevice) error {
359	g := m.G()
360	defer g.switchUserMu.Acquire(m, "switchUserNewConfig")()
361	cw := g.Env.GetConfigWriter()
362	if cw == nil {
363		return NoConfigWriterError{}
364	}
365	// Note that `true` here means that an existing user config entry will
366	// be overwritten.
367	if err := cw.SetUserConfig(NewUserConfig(u, n, salt, d), true /* overwrite */); err != nil {
368		return err
369	}
370	// Clear stayLoggedOut, so that if the service restarts for any reason
371	// we will know that we are logged in.
372	if g.Env.GetStayLoggedOut() {
373		if err := cw.SetStayLoggedOut(false); err != nil {
374			return err
375		}
376	}
377	return g.ActiveDevice.SetOrClear(m, ad)
378}
379
380// SwitchUserNewConfigActiveDevice creates a new config file stanza and an
381// active device for the given user, all while holding the switchUserMu lock.
382func (m MetaContext) SwitchUserNewConfigActiveDevice(uv keybase1.UserVersion, n NormalizedUsername, salt []byte, d keybase1.DeviceID, sigKey GenericKey, encKey GenericKey, deviceName string, keychainMode KeychainMode) error {
383	ad := NewProvisionalActiveDevice(m, uv, d, sigKey, encKey, deviceName, keychainMode)
384	return m.switchUserNewConfig(uv.Uid, n, salt, d, ad)
385}
386
387// SwitchUserNukeConfig removes the given username from the config file, and
388// then switches to not having a current user (by clearing the ActiveDevice,
389// etc). It does this in a critical section, holding switchUserMu.
390func (m MetaContext) SwitchUserNukeConfig(n NormalizedUsername) error {
391	g := m.G()
392	defer g.switchUserMu.Acquire(m, "SwitchUserNukeConfig")()
393	cw := g.Env.GetConfigWriter()
394	cr := g.Env.GetConfig()
395	if cw == nil {
396		return NoConfigWriterError{}
397	}
398	if cr == nil {
399		return NoConfigFileError{}
400	}
401	uid := cr.GetUIDForUsername(n)
402	err := cw.NukeUser(n)
403	if err != nil {
404		return err
405	}
406	if g.ActiveDevice.UID().Equal(uid) {
407		err := g.ActiveDevice.Clear()
408		if err != nil {
409			return err
410		}
411	}
412	return nil
413}
414
415func (m MetaContext) SwitchUser(n NormalizedUsername) error {
416	return m.SwitchUserToActiveDevice(n, nil)
417}
418
419func (m MetaContext) SwitchUserToActiveDevice(n NormalizedUsername, ad *ActiveDevice) (err error) {
420
421	defer m.Trace(fmt.Sprintf("MetaContext#SwitchUserToActiveDevice(%s,ActiveDevice:%v)", n.String(), (ad != nil)), &err)()
422
423	g := m.G()
424	if n.IsNil() {
425		return nil
426	}
427	if !n.IsValid() {
428		return NewBadUsernameError(n.String())
429	}
430	defer g.switchUserMu.Acquire(m, "SwitchUserToActiveDevice %v", n)()
431	cw := g.Env.GetConfigWriter()
432	if cw == nil {
433		return NoConfigWriterError{}
434	}
435	err = cw.SwitchUser(n)
436	if _, ok := err.(UserNotFoundError); ok {
437		m.Debug("| No user %s found; clearing out config", n)
438		err = nil
439	}
440	if err != nil {
441		return err
442	}
443	err = g.ActiveDevice.SetOrClear(m, ad)
444	if err != nil {
445		return err
446	}
447	m.CommitProvisionalLogin()
448
449	return nil
450}
451
452func (m MetaContext) SwitchUserDeprovisionNukeConfig(username NormalizedUsername) error {
453	g := m.G()
454	defer g.switchUserMu.Acquire(m, "SwitchUserDeprovisionNukeConfig %v", username)()
455
456	cw := g.Env.GetConfigWriter()
457	if cw == nil {
458		return NoConfigWriterError{}
459	}
460	if err := cw.NukeUser(username); err != nil {
461		return err
462	}
463
464	// The config entries we just nuked could still be in memory. Clear them.
465	return cw.SetUserConfig(nil, true /* overwrite; ignored */)
466}
467
468// SetActiveOneshotDevice acquires the switchUserMu mutex, setting the active
469// device to one that corresponds to the given UID and DeviceWithKeys, and also
470// sets the config file to a temporary in-memory config (not writing to disk)
471// to satisfy local requests for g.Env.*
472func (m MetaContext) SwitchUserToActiveOneshotDevice(uv keybase1.UserVersion, nun NormalizedUsername, d *DeviceWithKeys) (err error) {
473	defer m.Trace("MetaContext#SwitchUserToActiveOneshotDevice", &err)()
474
475	g := m.G()
476	defer g.switchUserMu.Acquire(m, "SwitchUserToActiveOneshotDevice")()
477	cw := g.Env.GetConfigWriter()
478	if cw == nil {
479		return NoConfigWriterError{}
480	}
481	ad := d.ToProvisioningKeyActiveDevice(m, uv)
482	err = g.ActiveDevice.Copy(m, ad)
483	if err != nil {
484		return err
485	}
486	uc := NewOneshotUserConfig(uv.Uid, nun, nil, d.DeviceID())
487	err = cw.SetUserConfig(uc, false)
488	if err != nil {
489		return err
490	}
491	return nil
492}
493
494// SwitchUserLoggedOut clears the active device and the current_user stanza of
495// the config file, all while holding the switchUserMu
496func (m MetaContext) SwitchUserLoggedOut() (err error) {
497	defer m.Trace("MetaContext#SwitchUserLoggedOut", &err)()
498	g := m.G()
499	defer g.switchUserMu.Acquire(m, "SwitchUserLoggedOut")()
500	cw := g.Env.GetConfigWriter()
501	if cw == nil {
502		return NoConfigWriterError{}
503	}
504	err = g.ActiveDevice.Clear()
505	if err != nil {
506		return err
507	}
508	err = cw.SetUserConfig(nil, false)
509	if err != nil {
510		return err
511	}
512	return nil
513}
514
515// SetActiveDevice sets the active device to have the UserVersion, deviceID,
516// sigKey, encKey and deviceName as specified, and does so while grabbing the
517// global switchUser lock, since it should be sycnhronized with attempts to
518// switch the global logged in user. It does not, however, change the
519// `current_user` in the config file, or edit the global config file in any
520// way.
521func (m MetaContext) SetActiveDevice(uv keybase1.UserVersion, deviceID keybase1.DeviceID,
522	sigKey, encKey GenericKey, deviceName string, keychainMode KeychainMode) error {
523	g := m.G()
524	defer g.switchUserMu.Acquire(m, "SetActiveDevice")()
525	if !g.Env.GetUID().Equal(uv.Uid) {
526		return NewUIDMismatchError("UID switched out from underneath provisioning process")
527	}
528	return g.ActiveDevice.Set(m, uv, deviceID, sigKey, encKey, deviceName, 0, keychainMode)
529}
530
531func (m MetaContext) SetSigningKey(uv keybase1.UserVersion, deviceID keybase1.DeviceID, sigKey GenericKey, deviceName string) error {
532	g := m.G()
533	defer g.switchUserMu.Acquire(m, "SetSigningKey")()
534	return g.ActiveDevice.setSigningKey(g, uv, deviceID, sigKey, deviceName)
535}
536
537func (m MetaContext) SetEncryptionKey(uv keybase1.UserVersion, deviceID keybase1.DeviceID, encKey GenericKey) error {
538	g := m.G()
539	defer g.switchUserMu.Acquire(m, "SetEncryptionKey")()
540	return g.ActiveDevice.setEncryptionKey(uv, deviceID, encKey)
541}
542
543// LogoutAndDeprovisionIfRevoked loads the user and checks if the current
544// device keys have been revoked. If so, it calls Logout and then runs the
545// ClearSecretsOnDeprovision
546func (m MetaContext) LogoutAndDeprovisionIfRevoked() (err error) {
547	m = m.WithLogTag("LOIR")
548
549	defer m.Trace("GlobalContext#LogoutAndDeprovisionIfRevoked", &err)()
550
551	if !m.ActiveDevice().Valid() {
552		m.Debug("LogoutAndDeprovisionIfRevoked: skipping check (not logged in)")
553		return nil
554	}
555
556	if m.G().Env.GetSkipLogoutIfRevokedCheck() {
557		m.Debug("LogoutAndDeprovisionIfRevoked: skipping check (SkipLogoutIfRevokedCheck)")
558		return nil
559	}
560
561	doLogout := false
562	err = CheckCurrentUIDDeviceID(m)
563	switch err.(type) {
564	case nil:
565		m.Debug("LogoutAndDeprovisionIfRevoked: current device ok")
566	case DeviceNotFoundError:
567		m.Debug("LogoutAndDeprovisionIfRevoked: device not found error; user was likely reset; calling logout (%s)", err)
568		doLogout = true
569	case KeyRevokedError:
570		m.Debug("LogoutAndDeprovisionIfRevoked: key revoked error error; device was revoked; calling logout (%s)", err)
571		doLogout = true
572	default:
573		m.Debug("LogoutAndDeprovisionIfRevoked: non-actionable error: %s", err)
574	}
575
576	if doLogout {
577		username := m.G().Env.GetUsername()
578		if err := m.LogoutWithOptions(LogoutOptions{KeepSecrets: false, Force: true}); err != nil {
579			return err
580		}
581		return ClearSecretsOnDeprovision(m, username)
582	}
583
584	return nil
585}
586
587func (m MetaContext) PassphraseStream() *PassphraseStream {
588	if m.LoginContext() != nil {
589		if m.LoginContext().PassphraseStreamCache() == nil {
590			return nil
591		}
592		return m.LoginContext().PassphraseStreamCache().PassphraseStream()
593	}
594	return m.ActiveDevice().PassphraseStream()
595}
596
597func (m MetaContext) PassphraseStreamAndTriplesec() (*PassphraseStream, Triplesec) {
598	var ppsc *PassphraseStreamCache
599	if m.LoginContext() != nil {
600		ppsc = m.LoginContext().PassphraseStreamCache()
601	} else {
602		ppsc = m.ActiveDevice().PassphraseStreamCache()
603	}
604	if ppsc == nil {
605		return nil, nil
606	}
607	return ppsc.PassphraseStreamAndTriplesec()
608}
609
610func (m MetaContext) TriplesecAndGeneration() (ret Triplesec, ppgen PassphraseGeneration) {
611	var pps *PassphraseStream
612	pps, ret = m.PassphraseStreamAndTriplesec()
613	if pps == nil {
614		return nil, ppgen
615	}
616	ppgen = pps.Generation()
617	if ppgen.IsNil() {
618		return nil, ppgen
619	}
620	return ret, ppgen
621}
622
623func (m MetaContext) CurrentUsername() NormalizedUsername {
624	if m.LoginContext() != nil {
625		return m.LoginContext().GetUsername()
626	}
627	return m.ActiveDevice().Username(m)
628}
629
630func (m MetaContext) CurrentUID() keybase1.UID {
631	if m.LoginContext() != nil {
632		return m.LoginContext().GetUID()
633	}
634	return m.ActiveDevice().UID()
635}
636
637func (m MetaContext) CurrentUserVersion() keybase1.UserVersion {
638	if m.LoginContext() != nil {
639		return m.LoginContext().GetUserVersion()
640	}
641	return m.ActiveDevice().UserVersion()
642}
643
644func (m MetaContext) HasAnySession() (ret bool) {
645	defer m.Trace("MetaContext#HasAnySession", nil)()
646	if m.LoginContext() != nil {
647		ok, _ := m.LoginContext().LoggedInLoad()
648		if ok {
649			m.Debug("| has temporary login session")
650			return true
651		}
652	}
653
654	if m.ActiveDevice().Valid() {
655		m.Debug("| has valid device")
656		return true
657	}
658
659	return false
660}
661
662func (m MetaContext) SyncSecrets() (ss *SecretSyncer, err error) {
663	defer m.Trace("MetaContext#SyncSecrets", &err)()
664	if m.LoginContext() != nil {
665		err = m.LoginContext().RunSecretSyncer(m, keybase1.UID(""))
666		if err != nil {
667			return nil, err
668		}
669		return m.LoginContext().SecretSyncer(), nil
670	}
671	return m.ActiveDevice().SyncSecrets(m)
672}
673
674func (m MetaContext) SyncSecretsForUID(u keybase1.UID) (ss *SecretSyncer, err error) {
675	defer m.Trace("MetaContext#SyncSecrets", &err)()
676	return m.ActiveDevice().SyncSecretsForUID(m, u, false /* force */)
677}
678
679func (m MetaContext) ProvisionalSessionArgs() (token string, csrf string) {
680	if m.LoginContext() == nil {
681		return "", ""
682	}
683	sess := m.LoginContext().LocalSession()
684	if sess == nil || !sess.IsValid() {
685		return "", ""
686	}
687	return sess.token, sess.csrf
688}
689
690func (m MetaContext) Keyring() (ret *SKBKeyringFile, err error) {
691	defer m.Trace("MetaContext#Keyring", &err)()
692	if m.LoginContext() != nil {
693		return m.LoginContext().Keyring(m)
694	}
695	return m.ActiveDevice().Keyring(m)
696}
697
698var _ logger.ContextInterface = MetaContext{}
699
700func (m MetaContext) UpdateContextToLoggerContext(c context.Context) logger.ContextInterface {
701	return m.WithContext(c)
702}
703
704func (m MetaContext) DebugStack() {
705	m.Debug("stack trace:\n%s", string(debug.Stack()))
706}
707