diff --git a/.gitignore b/.gitignore index 713b439..2cf324c 100644 --- a/.gitignore +++ b/.gitignore @@ -24,12 +24,11 @@ _testmain.go *.prof vendor/* -# vendor management -vendor/src/* -vendor/pkg/* bin/* +sigtool .??*.sw? *.pub *.key *.sig + diff --git a/crypt.go b/crypt.go index 51fb680..8335960 100644 --- a/crypt.go +++ b/crypt.go @@ -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 = "" } - 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 } diff --git a/die.go b/die.go new file mode 100644 index 0000000..40db351 --- /dev/null +++ b/die.go @@ -0,0 +1,55 @@ +// die.go -- die() and warn() +// +// (c) 2016 Sudhi Herle +// +// 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) +} diff --git a/sigtool.go b/sigtool.go index da6dc5b..697d69b 100644 --- a/sigtool.go +++ b/sigtool.go @@ -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" diff --git a/tests.sh b/tests.sh old mode 100644 new mode 100755