github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/bug_3964_repairman.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package libkb 5 6 import ( 7 "errors" 8 "time" 9 ) 10 11 type bug3964Repairman struct { 12 Contextified 13 } 14 15 func newBug3964Repairman(g *GlobalContext) *bug3964Repairman { 16 return &bug3964Repairman{Contextified: NewContextified(g)} 17 } 18 19 func (b *bug3964Repairman) attemptRepair(m MetaContext, lksec *LKSec, dkm DeviceKeyMap) (ran bool, serverHalfSet *LKSecServerHalfSet, err error) { 20 defer m.Trace("bug3964Repairman#attemptRepair", &err)() 21 var oldKeyring, newKeyring *SKBKeyringFile 22 lctx := m.LoginContext() 23 oldKeyring, err = lctx.Keyring(m) 24 if err != nil { 25 return false, nil, err 26 } 27 newKeyring, serverHalfSet, err = oldKeyring.Bug3964Repair(m, lksec, dkm) 28 if err != nil { 29 return false, nil, err 30 } 31 if newKeyring == nil { 32 return false, nil, nil 33 } 34 if err = newKeyring.Save(); err != nil { 35 m.Debug("Error saving new keyring: %s", err) 36 return false, nil, err 37 } 38 lctx.ClearKeyring() 39 return true, serverHalfSet, err 40 } 41 42 func (b *bug3964Repairman) loadLKSecServerDetails(m MetaContext, lksec *LKSec) (ret DeviceKeyMap, err error) { 43 defer m.Trace("bug3964Repairman#loadLKSecServerDetails", &err)() 44 ret, err = lksec.LoadServerDetails(m) 45 if err != nil { 46 return nil, err 47 } 48 lksec.SetFullSecret(m) 49 return ret, err 50 } 51 52 func (b *bug3964Repairman) updateSecretStore(m MetaContext, nun NormalizedUsername, lksec *LKSec) error { 53 fs := lksec.FullSecret() 54 ss := b.G().SecretStore() 55 if fs.IsNil() { 56 m.Warning("Got unexpected nil full secret") 57 return ss.ClearSecret(m, nun) 58 } 59 return ss.StoreSecret(m, nun, fs) 60 } 61 62 func (b *bug3964Repairman) saveRepairmanVisit(nun NormalizedUsername) (err error) { 63 defer b.G().Trace("bug3964Repairman#saveRepairmanVisit", &err)() 64 return b.G().Env.GetConfigWriter().SetBug3964RepairTime(nun, time.Now()) 65 } 66 67 func (b *bug3964Repairman) postToServer(m MetaContext, serverHalfSet *LKSecServerHalfSet, ppgen PassphraseGeneration, nun NormalizedUsername) (err error) { 68 defer m.G().CTrace(m.Ctx(), "bug3964Repairman#postToServer", &err)() 69 if serverHalfSet == nil { 70 return errors.New("internal error --- had nil server half set") 71 } 72 _, err = m.G().API.Post(m, APIArg{ 73 Endpoint: "user/bug_3964_repair", 74 SessionType: APISessionTypeREQUIRED, 75 Args: HTTPArgs{ 76 "device_id": S{Val: m.G().Env.GetDeviceIDForUsername(nun).String()}, 77 "ppgen": I{Val: int(ppgen)}, 78 "lks_server_halves": S{Val: serverHalfSet.EncodeToHexList()}, 79 }, 80 }) 81 return err 82 } 83 84 func (b *bug3964Repairman) computeShortCircuit(nun NormalizedUsername) (ss bool, err error) { 85 defer b.G().Trace("bug3964Repairman#computeShortCircuit", &err)() 86 repairTime, tmpErr := b.G().Env.GetConfig().GetBug3964RepairTime(nun) 87 88 // Ignore any decoding errors 89 if tmpErr != nil { 90 b.G().Log.Warning("Problem reading previous bug 3964 repair time: %s", tmpErr) 91 } 92 93 if repairTime.IsZero() { 94 b.G().Log.Debug("| repair time is zero or wasn't set") 95 return false, nil 96 } 97 var fileTime time.Time 98 fileTime, err = StatSKBKeyringMTime(nun, b.G()) 99 if err != nil { 100 return false, err 101 } 102 ss = !repairTime.Before(fileTime) 103 b.G().Log.Debug("| Checking repair-time (%s) v file-write-time (%s): shortCircuit=%v", repairTime, fileTime, ss) 104 return ss, nil 105 } 106 107 func (b *bug3964Repairman) fixLKSClientHalf(m MetaContext, lksec *LKSec, ppgen PassphraseGeneration) (err error) { 108 defer m.Trace("bug3964Repairman#fixLKSClientHalf", &err)() 109 var me *User 110 var encKey GenericKey 111 var ctext string 112 113 me, err = LoadMe(NewLoadUserArgWithMetaContext(m)) 114 if err != nil { 115 return err 116 } 117 encKey, err = me.GetDeviceSubkey() 118 if err != nil { 119 return err 120 } 121 // make client half recovery 122 kid := encKey.GetKID() 123 ctext, err = lksec.EncryptClientHalfRecovery(encKey) 124 if err != nil { 125 return err 126 } 127 128 _, err = b.G().API.Post(m, APIArg{ 129 Endpoint: "device/update_lks_client_half", 130 SessionType: APISessionTypeREQUIRED, 131 Args: HTTPArgs{ 132 "ppgen": I{Val: int(ppgen)}, 133 "kid": S{Val: kid.String()}, 134 "lks_client_half": S{Val: ctext}, 135 }, 136 }) 137 138 return err 139 } 140 141 // Run the engine 142 func (b *bug3964Repairman) Run(m MetaContext) (err error) { 143 defer m.G().CTrace(m.Ctx(), "bug3964Repairman#Run", &err)() 144 lctx := m.LoginContext() 145 pps := lctx.PassphraseStreamCache().PassphraseStream() 146 147 var lksec *LKSec 148 var ran bool 149 var dkm DeviceKeyMap 150 var ss bool 151 var serverHalfSet *LKSecServerHalfSet 152 nun := m.G().Env.GetUsername() 153 154 if m.G().TestOptions.NoBug3964Repair { 155 m.G().Log.CDebugf(m.Ctx(), "| short circuit due to test options") 156 return nil 157 } 158 159 if pps == nil { 160 m.G().Log.CDebugf(m.Ctx(), "| Can't run repairman without a passphrase stream") 161 return nil 162 } 163 164 if ss, err = b.computeShortCircuit(nun); err != nil { 165 return err 166 } 167 168 if ss { 169 // This logline is asserted in testing in bug_3964_repairman_test 170 m.G().Log.CDebugf(m.Ctx(), "| Repairman already visited after file update; bailing out") 171 return nil 172 } 173 174 // This logline is asserted in testing in bug_3964_repairman_test 175 m.G().Log.CDebugf(m.Ctx(), "| Repairman wasn't short-circuited") 176 177 lksec, err = pps.ToLKSec(lctx.GetUID()) 178 if err != nil { 179 return err 180 } 181 182 if dkm, err = b.loadLKSecServerDetails(m, lksec); err != nil { 183 return err 184 } 185 186 if ran, serverHalfSet, err = b.attemptRepair(m, lksec, dkm); err != nil { 187 return err 188 } 189 190 if err != nil { 191 return err 192 } 193 194 m.G().Log.CDebugf(m.Ctx(), "| SKB keyring repair completed; edits=%v", ran) 195 196 if !ran { 197 return b.saveRepairmanVisit(nun) 198 } 199 200 if err := b.fixLKSClientHalf(m, lksec, pps.Generation()); err != nil { 201 return err 202 } 203 204 if ussErr := b.updateSecretStore(m, nun, lksec); ussErr != nil { 205 m.G().Log.CWarningf(m.Ctx(), "Error in secret store manipulation: %s", ussErr) 206 } else { 207 err := b.saveRepairmanVisit(nun) 208 if err != nil { 209 return err 210 } 211 } 212 213 err = b.postToServer(m, serverHalfSet, pps.Generation(), nun) 214 215 return err 216 } 217 218 func RunBug3964Repairman(m MetaContext) error { 219 err := newBug3964Repairman(m.G()).Run(m) 220 if err != nil { 221 m.G().Log.CDebugf(m.Ctx(), "Error running Bug 3964 repairman: %s", err) 222 } 223 return err 224 }