Minor cleanups and one bugfix:

* bugfix: use os.IsNotExist() instead of comparing errors for equality;
  this fixes incorrect handling of missing authorized_keys file.
* move die() and warn() into die.go - and make them public functions.
* teach die.go to also provide atexit() like functionality
* teach all callers of sign.SafeFile{} to use AtExit() to delete
  temporary artifacts
* symbol renaming: die->Die, warn->Warn.
This commit is contained in:
Sudhi Herle 2022-05-12 16:53:27 -07:00
parent 42bbe5ddeb
commit 0ddf48c92f
5 changed files with 126 additions and 75 deletions

5
.gitignore vendored
View file

@ -24,12 +24,11 @@ _testmain.go
*.prof
vendor/*
# vendor management
vendor/src/*
vendor/pkg/*
bin/*
sigtool
.??*.sw?
*.pub
*.key
*.sig

View file

@ -48,7 +48,7 @@ func encrypt(args []string) {
err := fs.Parse(args)
if err != nil {
die("%s", err)
Die("%s", err)
}
var pws, infile string
@ -64,19 +64,19 @@ func encrypt(args []string) {
} else {
pws, err = utils.Askpass("Enter passphrase for private key", false)
if err != nil {
die("%s", err)
Die("%s", err)
}
}
return []byte(pws), nil
})
if err != nil {
die("%s", err)
Die("%s", err)
}
}
args = fs.Args()
if len(args) < 2 {
die("Insufficient args. Try '%s --help'", os.Args[0])
Die("Insufficient args. Try '%s --help'", os.Args[0])
}
var infd io.Reader = os.Stdin
@ -96,14 +96,14 @@ func encrypt(args []string) {
// Lets try to read the authorized files
home, err := os.UserHomeDir()
if err != nil {
die("can't find homedir for this user")
Die("can't find homedir for this user")
}
authkeys := fmt.Sprintf("%s/.ssh/authorized_keys", home)
authdata, err := ioutil.ReadFile(authkeys)
if err != nil {
if err != os.ErrNotExist {
die("can't open %s: %s", authkeys, err)
if !os.IsNotExist(err) {
Die("can't open %s: %s", authkeys, err)
}
}
@ -122,30 +122,32 @@ func encrypt(args []string) {
var ist, ost os.FileInfo
if ost, err = os.Stat(outfile); err != nil {
die("can't stat %s: %s", outfile, err)
Die("can't stat %s: %s", outfile, err)
}
if ist, err = inf.Stat(); err != nil {
die("can't stat %s: %s", infile, err)
Die("can't stat %s: %s", infile, err)
}
if os.SameFile(ist, ost) {
die("won't create output file: same as input file!")
Die("won't create output file: same as input file!")
}
mode = ist.Mode()
}
sf, err := sign.NewSafeFile(outfile, force, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
if err != nil {
die("%s", err)
Die("%s", err)
}
AtExit(sf.Abort)
defer sf.Abort()
outfd = sf
}
en, err := sign.NewEncryptor(sk, blksize)
if err != nil {
die("%s", err)
Die("%s", err)
}
errs := 0
@ -158,14 +160,14 @@ func encrypt(args []string) {
var ok bool
pk, ok = keymap[fn]
if !ok {
warn("can't find user %s in %s", fn, authkeys)
Warn("can't find user %s in %s", fn, authkeys)
errs += 1
continue
}
} else {
pk, err = sign.ReadPublicKey(fn)
if err != nil {
warn("%s", err)
Warn("%s", err)
errs += 1
continue
}
@ -173,17 +175,17 @@ func encrypt(args []string) {
err = en.AddRecipient(pk)
if err != nil {
die("%s", err)
Die("%s", err)
}
}
if errs > 0 {
die("Too many errors!")
Die("Too many errors!")
}
err = en.Encrypt(infd, outfd)
if err != nil {
die("%s", err)
Die("%s", err)
}
outfd.Close()
}
@ -221,12 +223,12 @@ func decrypt(args []string) {
err := fs.Parse(args)
if err != nil {
die("%s", err)
Die("%s", err)
}
args = fs.Args()
if len(args) < 1 {
die("Insufficient args. Try '%s --help'", os.Args[0])
Die("Insufficient args. Try '%s --help'", os.Args[0])
}
var infd io.Reader = os.Stdin
@ -246,13 +248,13 @@ func decrypt(args []string) {
} else {
pws, err = utils.Askpass("Enter passphrase for private key", false)
if err != nil {
die("%s", err)
Die("%s", err)
}
}
return []byte(pws), nil
})
if err != nil {
die("%s", err)
Die("%s", err)
}
var pk *sign.PublicKey
@ -260,7 +262,7 @@ func decrypt(args []string) {
if len(pubkey) > 0 {
pk, err = sign.ReadPublicKey(pubkey)
if err != nil {
die("%s", err)
Die("%s", err)
}
}
@ -284,33 +286,35 @@ func decrypt(args []string) {
var err error
if ost, err = os.Stat(outfile); err != nil {
die("can't stat %s: %s", outfile, err)
Die("can't stat %s: %s", outfile, err)
}
if ist, err = inf.Stat(); err != nil {
die("can't stat %s: %s", infile, err)
Die("can't stat %s: %s", infile, err)
}
if os.SameFile(ist, ost) {
die("won't create output file: same as input file!")
Die("won't create output file: same as input file!")
}
mode = ist.Mode()
}
sf, err := sign.NewSafeFile(outfile, force, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
if err != nil {
die("%s", err)
Die("%s", err)
}
AtExit(sf.Abort)
defer sf.Abort()
outfd = sf
}
d, err := sign.NewDecryptor(infd)
if err != nil {
die("%s", err)
Die("%s", err)
}
err = d.SetPrivateKey(sk, pk)
if err != nil {
die("%s", err)
Die("%s", err)
}
if pk == nil && d.AuthenticatedSender() {
@ -318,17 +322,17 @@ func decrypt(args []string) {
if len(fn) == 0 || fn == "-" {
fn = "<stdin>"
}
warn("%s: Missing sender Public Key; can't authenticate sender ..", fn)
Warn("%s: Missing sender Public Key; can't authenticate sender ..", fn)
}
if err = d.Decrypt(outfd); err != nil {
die("%s", err)
Die("%s", err)
}
outfd.Close()
if test {
warn("Enc file OK")
Warn("Enc file OK")
}
}
@ -338,9 +342,16 @@ func encryptUsage(fs *flag.FlagSet) {
Usage: %s encrypt [options] to [to ...] infile|-
Where TO is the public key of the recipient and INFILE is an input file.
If the input file is '-' then %s reads from STDIN. Unless '-o' is used,
%s writes the encrypted output to STDOUT.
Where TO is the public key of the recipient; it can be one of:
- a file referring to an SSH or sigtool public key.
- string of the form 'a@b' - in which case the user's default
ssh/authorized_keys is consulted to find the comment matching
'a@b' - in which case the user's ssh authorized_keys file is consulted to
find the comment matching the string.
INFILE is an input file to be encrypted. If the input file is '-' then %s
reads from STDIN. Unless '-o' is used, %s writes the encrypted output to STDOUT.
Options:
`, Z, Z, Z, Z)
@ -368,7 +379,7 @@ Options:
func mustOpen(fn string, flag int) *os.File {
fdk, err := os.OpenFile(fn, flag, 0600)
if err != nil {
die("can't open file %s: %s", fn, err)
Die("can't open file %s: %s", fn, err)
}
return fdk
}

55
die.go Normal file
View file

@ -0,0 +1,55 @@
// die.go -- die() and warn()
//
// (c) 2016 Sudhi Herle <sudhi@herle.net>
//
// Licensing Terms: GPLv2
//
// If you need a commercial license for this work, please contact
// the author.
//
// This software does not come with any express or implied
// warranty; it is provided "as is". No claim is made to its
// suitability for any purpose.
package main
import (
"fmt"
"os"
)
var atExit []func()
// Die prints an error message to stderr
// and exits the program after calling all the registered
// at-exit functions.
func Die(f string, v ...interface{}) {
Warn(f, v...)
Exit(1)
}
// Warn prints an error message to stderr
func Warn(f string, v ...interface{}) {
z := fmt.Sprintf("%s: %s", os.Args[0], f)
s := fmt.Sprintf(z, v...)
if n := len(s); s[n-1] != '\n' {
s += "\n"
}
os.Stderr.WriteString(s)
os.Stderr.Sync()
}
// AtExit registers a function to be called before the program exits.
func AtExit(f func()) {
atExit = append(atExit, f)
}
// Exit invokes the registered atexit handlers and exits with the
// given code.
func Exit(v int) {
for _, f := range atExit {
f()
}
os.Exit(v)
}

View file

@ -49,8 +49,7 @@ func main() {
args := mf.Args()
if len(args) < 1 {
warn("Insufficient arguments. Try '%s -h'", Z)
os.Exit(1)
Die("Insufficient arguments. Try '%s -h'", Z)
}
cmds := map[string]func(args []string){
@ -73,15 +72,18 @@ func main() {
ab := utils.Abbrev(words)
canon, ok := ab[strings.ToLower(args[0])]
if !ok {
die("Unknown command %s", args[0])
Die("Unknown command %s", args[0])
}
cmd := cmds[canon]
if cmd == nil {
die("can't map command %s", canon)
Die("can't map command %s", canon)
}
cmd(args[1:])
// always call Exit so that at-exit handlers are called.
Exit(0)
}
// Run the generate command
@ -115,7 +117,7 @@ Options:
args = fs.Args()
if len(args) < 1 {
die("Insufficient arguments to 'generate'. Try '%s generate -h' ..", Z)
Die("Insufficient arguments to 'generate'. Try '%s generate -h' ..", Z)
}
bn := args[0]
@ -125,7 +127,7 @@ Options:
if !force {
if exists(pkn) || exists(skn) {
die("Public/Private key files (%s, %s) exist. won't overwrite!", skn, pkn)
Die("Public/Private key files (%s, %s) exist. won't overwrite!", skn, pkn)
}
}
@ -139,7 +141,7 @@ Options:
} else {
pws, err = utils.Askpass("Enter passphrase for private key", true)
if err != nil {
die("%s", err)
Die("%s", err)
}
}
@ -148,16 +150,16 @@ Options:
sk, err := sign.NewPrivateKey()
if err != nil {
die("%s", err)
Die("%s", err)
}
if err = sk.Serialize(skn, comment, force, pw); err != nil {
die("%s", err)
Die("%s", err)
}
pk := sk.PublicKey()
if err = pk.Serialize(pkn, comment, force); err != nil {
die("%s", err)
Die("%s", err)
}
}
@ -190,7 +192,7 @@ Options:
args = fs.Args()
if len(args) < 2 {
die("Insufficient arguments to 'sign'. Try '%s sign -h' ..", Z)
Die("Insufficient arguments to 'sign'. Try '%s sign -h' ..", Z)
}
kn := args[0]
@ -208,10 +210,11 @@ Options:
if outf != "-" {
sf, err := sign.NewSafeFile(outf, force, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
die("can't create sig file: %s", err)
Die("can't create sig file: %s", err)
}
// we unlink and remove temp on any error
AtExit(sf.Abort)
defer sf.Abort()
fd = sf
}
@ -227,19 +230,19 @@ Options:
} else {
pws, err = utils.Askpass("Enter passphrase for private key", false)
if err != nil {
die("%s", err)
Die("%s", err)
}
}
return []byte(pws), nil
})
if err != nil {
die("%s", err)
Die("%s", err)
}
sig, err := sk.SignFile(fn)
if err != nil {
die("%s", err)
Die("%s", err)
}
sigbytes, err := sig.MarshalBinary(fmt.Sprintf("input=%s", fn))
@ -271,7 +274,7 @@ Options:
args = fs.Args()
if len(args) < 3 {
die("Insufficient arguments to 'verify'. Try '%s verify -h' ..", Z)
Die("Insufficient arguments to 'verify'. Try '%s verify -h' ..", Z)
}
pn := args[0]
@ -280,21 +283,21 @@ Options:
sig, err := sign.ReadSignature(sn)
if err != nil {
die("Can't read signature '%s': %s", sn, err)
Die("Can't read signature '%s': %s", sn, err)
}
pk, err := sign.ReadPublicKey(pn)
if err != nil {
die("%s", err)
Die("%s", err)
}
if !sig.IsPKMatch(pk) {
die("Wrong public key '%s' for verifying '%s'", pn, sn)
Die("Wrong public key '%s' for verifying '%s'", pn, sn)
}
ok, err := pk.VerifyFile(fn, sig)
if err != nil {
die("%s", err)
Die("%s", err)
}
exit := 0
@ -343,23 +346,6 @@ func exists(nm string) bool {
return false
}
// die with error
func die(f string, v ...interface{}) {
warn(f, v...)
os.Exit(1)
}
func warn(f string, v ...interface{}) {
z := fmt.Sprintf("%s: %s", os.Args[0], f)
s := fmt.Sprintf(z, v...)
if n := len(s); s[n-1] != '\n' {
s += "\n"
}
os.Stderr.WriteString(s)
os.Stderr.Sync()
}
// This will be filled in by "build"
var RepoVersion string = "UNDEFINED"
var Buildtime string = "UNDEFINED"

0
tests.sh Normal file → Executable file
View file