X-Git-Url: https://git.distorted.org.uk/~mdw/catacomb-python/blobdiff_plain/63ba6dfae101afe3102e0c3c3fb6318c34a207f8..18a7521a4f10a2de6d2741ef4db160374139acc6:/pwsafe diff --git a/pwsafe b/pwsafe index 50a5878..e622e53 100644 --- a/pwsafe +++ b/pwsafe @@ -39,6 +39,14 @@ import catacomb as C from catacomb.pwsafe import * ###-------------------------------------------------------------------------- +### Python version portability. + +def _text(bin): return bin +def _bin(text): return text + +def _excval(): return SYS.exc_info()[1] + +###-------------------------------------------------------------------------- ### Utilities. ## The program name. @@ -46,7 +54,7 @@ prog = re.sub(r'^.*[/\\]', '', argv[0]) def moan(msg): """Issue a warning message MSG.""" - print >>stderr, '%s: %s' % (prog, msg) + stderr.write('%s: %s\n' % (prog, msg)) def die(msg): """Report MSG as a fatal error, and exit.""" @@ -59,27 +67,33 @@ def die(msg): def cmd_create(av): ## Default crypto-primitive selections. - cipher = 'blowfish-cbc' - hash = 'rmd160' + cipher = 'rijndael-cbc' + hash = 'sha256' mac = None ## Parse the options. try: - opts, args = getopt(av, 'c:h:m:', ['cipher=', 'mac=', 'hash=']) + opts, args = getopt(av, 'c:d:h:m:', + ['cipher=', 'database=', 'mac=', 'hash=']) except GetoptError: return 1 + dbty = 'flat' for o, a in opts: if o in ('-c', '--cipher'): cipher = a elif o in ('-m', '--mac'): mac = a elif o in ('-h', '--hash'): hash = a - else: raise 'Barf!' + elif o in ('-d', '--database'): dbty = a + else: raise Exception("barf") if len(args) > 2: return 1 if len(args) >= 1: tag = args[0] else: tag = 'pwsafe' ## Set up the database. if mac is None: mac = hash + '-hmac' - PW.create(file, C.gcciphers[cipher], C.gchashes[hash], C.gcmacs[mac], tag) + try: dbcls = StorageBackend.byname(dbty) + except KeyError: die("Unknown database backend `%s'" % dbty) + PW.create(dbcls, file, tag, + C.gcciphers[cipher], C.gchashes[hash], C.gcmacs[mac]) def cmd_changepp(av): if len(av) != 0: return 1 @@ -88,8 +102,8 @@ def cmd_changepp(av): def cmd_find(av): if len(av) != 1: return 1 with PW(file) as pw: - try: print pw[av[0]] - except KeyError, exc: die("Password `%s' not found" % exc.args[0]) + try: print(_text(pw[_bin(av[0])])) + except KeyError: die("Password `%s' not found" % _excval().args[0]) def cmd_store(av): if len(av) < 1 or len(av) > 2: return 1 @@ -100,10 +114,10 @@ def cmd_store(av): vpp = C.getpass("Confirm passphrase `%s': " % tag) if pp != vpp: die("passphrases don't match") elif av[1] == '-': - pp = stdin.readline().rstrip('\n') + pp = _bin(stdin.readline().rstrip('\n')) else: - pp = av[1] - pw[av[0]] = pp + pp = _bin(av[1]) + pw[_bin(av[0])] = pp def cmd_copy(av): if len(av) < 1 or len(av) > 2: return 1 @@ -112,7 +126,8 @@ def cmd_copy(av): if len(av) >= 3: pat = av[1] else: pat = None for k in pw_in: - if pat is None or fnmatch(k, pat): pw_out[k] = pw_in[k] + ktext = _text(k) + if pat is None or fnmatch(ktext, pat): pw_out[k] = pw_in[k] def cmd_list(av): if len(av) > 1: return 1 @@ -120,16 +135,17 @@ def cmd_list(av): if len(av) >= 1: pat = av[0] else: pat = None for k in pw: - if pat is None or fnmatch(k, pat): print k + ktext = _text(k) + if pat is None or fnmatch(ktext, pat): print(ktext) def cmd_topixie(av): if len(av) > 2: return 1 with PW(file) as pw: pix = C.Pixie() if len(av) == 0: - for tag in pw: pix.set(tag, pw[tag]) + for tag in pw: pix.set(tag, pw[_bin(tag)]) else: - tag = av[0] + tag = _bin(av[0]) if len(av) >= 2: pptag = av[1] else: pptag = av[0] pix.set(pptag, pw[tag]) @@ -137,34 +153,56 @@ def cmd_topixie(av): def cmd_del(av): if len(av) != 1: return 1 with PW(file, writep = True) as pw: - tag = av[0] + tag = _bin(av[0]) try: del pw[tag] - except KeyError, exc: die("Password `%s' not found" % exc.args[0]) + except KeyError: die("Password `%s' not found" % _excval().args[0]) + +def cmd_xfer(av): + + ## Parse the command line. + try: opts, args = getopt(av, 'd:', ['database=']) + except GetoptError: return 1 + dbty = 'flat' + for o, a in opts: + if o in ('-d', '--database'): dbty = a + else: raise Exception("barf") + if len(args) != 1: return 1 + try: dbcls = StorageBackend.byname(dbty) + except KeyError: die("Unknown database backend `%s'" % dbty) + + ## Create the target database. + with StorageBackend.open(file) as db_in: + with dbcls.create(args[0]) as db_out: + for k, v in db_in.iter_meta(): db_out.put_meta(k, v) + for k, v in db_in.iter_passwds(): db_out.put_passwd(k, v) commands = { 'create': [cmd_create, - '[-c CIPHER] [-h HASH] [-m MAC] [PP-TAG]'], + '[-c CIPHER] [-d DBTYPE] [-h HASH] [-m MAC] [PP-TAG]'], 'find' : [cmd_find, 'LABEL'], 'store' : [cmd_store, 'LABEL [VALUE]'], 'list' : [cmd_list, '[GLOB-PATTERN]'], 'changepp' : [cmd_changepp, ''], 'copy' : [cmd_copy, 'DEST-FILE [GLOB-PATTERN]'], 'to-pixie' : [cmd_topixie, '[TAG [PIXIE-TAG]]'], - 'delete' : [cmd_del, 'TAG']} + 'delete' : [cmd_del, 'TAG'], + 'xfer': [cmd_xfer, '[-d DBTYPE] DEST-FILE'] } ###-------------------------------------------------------------------------- ### Command-line handling and dispatch. def version(): - print '%s 1.0.0' % prog + print('%s 1.0.0' % prog) + print('Backend types: %s' % + ' '.join([c.NAME for c in StorageBackend.classes()])) def usage(fp): - print >>fp, 'Usage: %s COMMAND [ARGS...]' % prog + fp.write('Usage: %s COMMAND [ARGS...]\n' % prog) def help(): version() - print + print('') usage(stdout) - print ''' + print(''' Maintains passwords or other short secrets securely. Options: @@ -176,9 +214,9 @@ Options: -f, --file=FILE Where to find the password-safe file. Commands provided: -''' +''') for c in sorted(commands): - print '%s %s' % (c, commands[c][1]) + print('%s %s' % (c, commands[c][1])) ## Choose a default database file. if 'PWSAFE' in environ: @@ -206,7 +244,7 @@ for o, a in opts: elif o in ('-f', '--file'): file = a else: - raise 'Barf!' + raise Exception("barf") if len(argv) < 1: usage(stderr) exit(1) @@ -219,7 +257,7 @@ else: c = 'find' try: if commands[c][0](argv): - print >>stderr, 'Usage: %s %s %s' % (prog, c, commands[c][1]) + stderr.write('Usage: %s %s %s\n' % (prog, c, commands[c][1])) exit(1) except DecryptError: die("decryption failure")