X-Git-Url: https://git.distorted.org.uk/~mdw/catacomb-python/blobdiff_plain/2e6a3fdaabb158cddb8fd1ac14737d6228274b37..40b16f0c70eee219206c99224088c3e81fed4d8a:/pwsafe?ds=sidebyside diff --git a/pwsafe b/pwsafe old mode 100755 new mode 100644 index 3485b08..043db43 --- a/pwsafe +++ b/pwsafe @@ -1,31 +1,90 @@ -#! /usr/bin/python2.2 -# -*-python-*- +#! /usr/bin/python +### -*-python-*- +### +### Tool for maintaining a secure-ish password database +### +### (c) 2005 Straylight/Edgeware +### + +###----- Licensing notice --------------------------------------------------- +### +### This file is part of the Python interface to Catacomb. +### +### Catacomb/Python is free software; you can redistribute it and/or modify +### it under the terms of the GNU General Public License as published by +### the Free Software Foundation; either version 2 of the License, or +### (at your option) any later version. +### +### Catacomb/Python is distributed in the hope that it will be useful, +### but WITHOUT ANY WARRANTY; without even the implied warranty of +### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +### GNU General Public License for more details. +### +### You should have received a copy of the GNU General Public License +### along with Catacomb/Python; if not, write to the Free Software Foundation, +### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +###--------------------------------------------------------------------------- +### Imported modules. -import catacomb as C -from catacomb.pwsafe import * import gdbm as G from os import environ from sys import argv, exit, stdin, stdout, stderr from getopt import getopt, GetoptError from fnmatch import fnmatch -import sre as re +import re + +import catacomb as C +from catacomb.pwsafe import * +###-------------------------------------------------------------------------- +### Utilities. + +## The program name. prog = re.sub(r'^.*[/\\]', '', argv[0]) + def moan(msg): + """Issue a warning message MSG.""" print >>stderr, '%s: %s' % (prog, msg) + def die(msg): + """Report MSG as a fatal error, and exit.""" moan(msg) exit(1) -if 'PWSAFE' in environ: - file = environ['PWSAFE'] -else: - file = '%s/.pwsafe' % environ['HOME'] +def chomp(pp): + """Return the string PP, without its trailing newline if it has one.""" + if len(pp) > 0 and pp[-1] == '\n': + pp = pp[:-1] + return pp + +def asciip(s): + """Answer whether all of the characters of S are plain ASCII.""" + for ch in s: + if ch < ' ' or ch > '~': return False + return True + +def present(s): + """ + Return a presentation form of the string S. + + If S is plain ASCII, then return S unchanged; otherwise return it as one of + Catacomb's ByteString objects. + """ + if asciip(s): return s + return C.ByteString(s) + +###-------------------------------------------------------------------------- +### Subcommand implementations. def cmd_create(av): + + ## Default crypto-primitive selections. cipher = 'blowfish-cbc' hash = 'rmd160' mac = None + + ## Parse the options. try: opts, args = getopt(av, 'c:h:m:', ['cipher=', 'mac=', 'hash=']) except GetoptError: @@ -45,28 +104,10 @@ def cmd_create(av): tag = args[0] else: tag = 'pwsafe' - db = G.open(file, 'n', 0600) - pp = C.ppread(tag, C.PMODE_VERIFY) - if not mac: mac = hash + '-hmac' - c = C.gcciphers[cipher] - h = C.gchashes[hash] - m = C.gcmacs[mac] - ppk = PW.PPK(pp, c, h, m) - ck = C.rand.block(c.keysz.default) - mk = C.rand.block(m.keysz.default) - k = Crypto(c, h, m, ck, mk) - db['tag'] = tag - db['salt'] = ppk.salt - db['cipher'] = cipher - db['hash'] = hash - db['mac'] = mac - db['key'] = ppk.encrypt(wrapstr(ck) + wrapstr(mk)) - db['magic'] = k.encrypt(C.rand.block(h.hashsz)) -def chomp(pp): - if len(pp) > 0 and pp[-1] == '\n': - pp = pp[:-1] - return pp + ## Set up the database. + if mac is None: mac = hash + '-hmac' + PW.create(file, C.gcciphers[cipher], C.gchashes[hash], C.gcmacs[mac], tag) def cmd_changepp(av): if len(av) != 0: @@ -150,13 +191,6 @@ def cmd_del(av): except KeyError, exc: die('Password `%s\' not found.' % exc.args[0]) -def asciip(s): - for ch in s: - if ch < ' ' or ch > '~': return False - return True -def present(s): - if asciip(s): return s - return C.ByteString(s) def cmd_dump(av): db = gdbm.open(file, 'r') k = db.firstkey() @@ -176,14 +210,19 @@ commands = { 'create': [cmd_create, 'delete' : [cmd_del, 'TAG'], 'dump' : [cmd_dump, '']} +###-------------------------------------------------------------------------- +### Command-line handling and dispatch. + def version(): print '%s 1.0.0' % prog + def usage(fp): print >>fp, 'Usage: %s COMMAND [ARGS...]' % prog + def help(): version() print - usage(stdout) + usage(stdout) print ''' Maintains passwords or other short secrets securely. @@ -200,9 +239,15 @@ Commands provided: for c in commands: print '%s %s' % (c, commands[c][1]) +## Choose a default database file. +if 'PWSAFE' in environ: + file = environ['PWSAFE'] +else: + file = '%s/.pwsafe' % environ['HOME'] + +## Parse the command-line options. try: - opts, argv = getopt(argv[1:], - 'hvuf:', + opts, argv = getopt(argv[1:], 'hvuf:', ['help', 'version', 'usage', 'file=']) except GetoptError: usage(stderr) @@ -225,11 +270,17 @@ if len(argv) < 1: usage(stderr) exit(1) +## Dispatch to a command handler. if argv[0] in commands: c = argv[0] argv = argv[1:] else: c = 'find' -if commands[c][0](argv): - print >>stderr, 'Usage: %s %s %s' % (prog, c, commands[c][1]) - exit(1) +try: + if commands[c][0](argv): + print >>stderr, 'Usage: %s %s %s' % (prog, c, commands[c][1]) + exit(1) +except DecryptError: + die("decryption failure") + +###----- That's all, folks --------------------------------------------------