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  }