github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/pgp_push_private.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 engine 5 6 import ( 7 "errors" 8 "fmt" 9 10 "github.com/keybase/client/go/libkb" 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 "github.com/keybase/go-framed-msgpack-rpc/rpc" 13 ) 14 15 type PGPPushPrivate struct { 16 arg keybase1.PGPPushPrivateArg 17 } 18 19 func (e *PGPPushPrivate) Name() string { 20 return "PGPPushPrivate" 21 } 22 23 func (e *PGPPushPrivate) Prereqs() Prereqs { 24 return Prereqs{} 25 } 26 27 func (e *PGPPushPrivate) RequiredUIs() []libkb.UIKind { 28 return []libkb.UIKind{} 29 } 30 31 func (e *PGPPushPrivate) SubConsumers() []libkb.UIConsumer { 32 return []libkb.UIConsumer{} 33 } 34 35 func NewPGPPushPrivate(arg keybase1.PGPPushPrivateArg) *PGPPushPrivate { 36 return &PGPPushPrivate{arg} 37 } 38 39 func getCurrentUserPGPKeys(m libkb.MetaContext) ([]libkb.PGPFingerprint, error) { 40 uid := m.CurrentUID() 41 if uid.IsNil() { 42 return nil, libkb.NewLoginRequiredError("for push/pull of PGP private keys to KBFS") 43 } 44 upk, _, err := m.G().GetUPAKLoader().LoadV2(libkb.NewLoadUserArgWithMetaContext(m).WithUID(uid)) 45 if err != nil { 46 return nil, err 47 } 48 var res []libkb.PGPFingerprint 49 for _, key := range upk.Current.PGPKeys { 50 if key.Base.Revocation != nil { 51 continue 52 } 53 res = append(res, libkb.PGPFingerprint(key.Fingerprint)) 54 } 55 if len(res) == 0 { 56 return nil, errors.New("The --all flag only works if you have PGP keys linked to your Keybase account") 57 } 58 return res, nil 59 } 60 61 func getPrivateFingerprints(m libkb.MetaContext, fps []keybase1.PGPFingerprint) ([]libkb.PGPFingerprint, error) { 62 if len(fps) == 0 { 63 return getCurrentUserPGPKeys(m) 64 } 65 66 var ret []libkb.PGPFingerprint 67 for _, fp := range fps { 68 ret = append(ret, libkb.ImportPGPFingerprint(fp)) 69 70 } 71 return ret, nil 72 } 73 74 func simpleFSClient(m libkb.MetaContext) (*keybase1.SimpleFSClient, error) { 75 xp := m.G().ConnectionManager.LookupByClientType(keybase1.ClientType_KBFS) 76 if xp == nil { 77 return nil, libkb.KBFSNotRunningError{} 78 } 79 return &keybase1.SimpleFSClient{ 80 Cli: rpc.NewClient(xp, libkb.NewContextifiedErrorUnwrapper(m.G()), nil), 81 }, nil 82 } 83 84 func (e *PGPPushPrivate) mkdir(m libkb.MetaContext, fs *keybase1.SimpleFSClient, path string) (err error) { 85 opid, err := fs.SimpleFSMakeOpid(m.Ctx()) 86 if err != nil { 87 return err 88 } 89 defer fs.SimpleFSClose(m.Ctx(), opid) 90 err = fs.SimpleFSOpen(m.Ctx(), keybase1.SimpleFSOpenArg{ 91 OpID: opid, 92 Dest: keybase1.NewPathWithKbfsPath(path), 93 Flags: keybase1.OpenFlags_DIRECTORY, 94 }) 95 return err 96 } 97 98 func (e *PGPPushPrivate) write(m libkb.MetaContext, fs *keybase1.SimpleFSClient, path string, data string) (err error) { 99 opid, err := fs.SimpleFSMakeOpid(m.Ctx()) 100 if err != nil { 101 return err 102 } 103 defer fs.SimpleFSClose(m.Ctx(), opid) 104 err = fs.SimpleFSOpen(m.Ctx(), keybase1.SimpleFSOpenArg{ 105 OpID: opid, 106 Dest: keybase1.NewPathWithKbfsPath(path), 107 Flags: keybase1.OpenFlags_WRITE, 108 }) 109 if err != nil { 110 return err 111 } 112 err = fs.SimpleFSWrite(m.Ctx(), keybase1.SimpleFSWriteArg{ 113 OpID: opid, 114 Offset: 0, 115 Content: []byte(data), 116 }) 117 if err != nil { 118 return err 119 } 120 return nil 121 } 122 123 func (e *PGPPushPrivate) link(m libkb.MetaContext, fs *keybase1.SimpleFSClient, file string, link string) (err error) { 124 err = fs.SimpleFSSymlink(m.Ctx(), keybase1.SimpleFSSymlinkArg{ 125 Target: file, 126 Link: keybase1.NewPathWithKbfsPath(link), 127 }) 128 return err 129 } 130 131 func (e *PGPPushPrivate) remove(m libkb.MetaContext, fs *keybase1.SimpleFSClient, file string) (err error) { 132 opid, err := fs.SimpleFSMakeOpid(m.Ctx()) 133 if err != nil { 134 return err 135 } 136 defer fs.SimpleFSClose(m.Ctx(), opid) 137 err = fs.SimpleFSRemove(m.Ctx(), keybase1.SimpleFSRemoveArg{ 138 OpID: opid, 139 Path: keybase1.NewPathWithKbfsPath(file), 140 }) 141 if err != nil { 142 return err 143 } 144 err = fs.SimpleFSWait(m.Ctx(), opid) 145 return err 146 } 147 148 func (e *PGPPushPrivate) linkExists(m libkb.MetaContext, fs *keybase1.SimpleFSClient, file string) (exists bool, err error) { 149 defer m.Trace(fmt.Sprintf("linkExists(%q)", file), &err)() 150 151 var dirent keybase1.Dirent 152 dirent, err = fs.SimpleFSStat(m.Ctx(), keybase1.SimpleFSStatArg{ 153 Path: keybase1.NewPathWithKbfsPath(file), 154 RefreshSubscription: false, 155 }) 156 if err != nil { 157 m.Debug("error accessing %s; assuming that the file doesn't exist (%s)", file, err.Error()) 158 return false, nil 159 } 160 if dirent.DirentType != keybase1.DirentType_SYM { 161 return false, fmt.Errorf("Cannot rewrite %s since it's not a symlink", file) 162 } 163 return true, nil 164 } 165 166 func (e *PGPPushPrivate) push(m libkb.MetaContext, fp libkb.PGPFingerprint, tty string, fs *keybase1.SimpleFSClient) error { 167 armored, err := m.G().GetGpgClient().ImportKeyArmored(m, true /* secret */, fp, tty) 168 if err != nil { 169 return err 170 } 171 172 username := m.CurrentUsername() 173 if username.IsNil() { 174 return libkb.NewLoginRequiredError("no username found") 175 } 176 177 path := "/private/" + username.String() + "/.keys" 178 179 // Make /.keys/pgp. If it already exists, these mkdir calls should not error out. 180 err = e.mkdir(m, fs, path) 181 if err != nil { 182 return err 183 } 184 path += "/pgp" 185 err = e.mkdir(m, fs, path) 186 if err != nil { 187 return err 188 } 189 190 filename := fp.String() + "-" + fmt.Sprintf("%d", m.G().Clock().Now().Unix()) + ".asc" 191 linkname := fp.String() + ".asc" 192 filepath := path + "/" + filename 193 linkpath := path + "/" + linkname 194 195 err = e.write(m, fs, filepath, armored) 196 if err != nil { 197 return err 198 } 199 200 linkExists, err := e.linkExists(m, fs, linkpath) 201 if err != nil { 202 return err 203 } 204 205 // Note the small chance of a race here, but it doesn't seem dangerous. 206 if linkExists { 207 err = e.remove(m, fs, linkpath) 208 if err != nil { 209 return err 210 } 211 } 212 213 err = e.link(m, fs, filename, linkpath) 214 return err 215 } 216 217 func (e *PGPPushPrivate) Run(m libkb.MetaContext) (err error) { 218 219 defer m.Trace("PGPPushPrivate#Run", &err)() 220 221 tty, err := m.UIs().GPGUI.GetTTY(m.Ctx()) 222 if err != nil { 223 return err 224 } 225 226 fingerprints, err := getPrivateFingerprints(m, e.arg.Fingerprints) 227 if err != nil { 228 return err 229 } 230 231 fs, err := simpleFSClient(m) 232 if err != nil { 233 return err 234 } 235 236 if len(fingerprints) == 0 { 237 return errors.New("no PGP keys provided") 238 } 239 240 for _, fp := range fingerprints { 241 err = e.push(m, fp, tty, fs) 242 if err != nil { 243 return err 244 } 245 } 246 247 return nil 248 }