From e345b51f3b1ed29ff7f4dab5f8e1a1496270e6a2 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Tue, 21 Mar 2006 11:52:41 +0000 Subject: [PATCH] cryptomail: Implement info and revoke commands. Organization: Straylight/Edgeware From: Mark Wooding This means we need to remember who made which token, and check that people don't find out about or revoke each others' tokens. --- bin/cryptomail | 116 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 99 insertions(+), 17 deletions(-) diff --git a/bin/cryptomail b/bin/cryptomail index 27beea2..3a24d04 100755 --- a/bin/cryptomail +++ b/bin/cryptomail @@ -221,12 +221,22 @@ class CMDB (AttrDB): (SELECT attrset FROM expiry WHERE time < ?)''', [now]) cur.execute('DELETE FROM expiry WHERE time < ?', [now]) + cur.execute('''DELETE FROM expiry WHERE attrset IN + (SELECT attrset + FROM expiry LEFT JOIN attrset + ON expiry.attrset = attrset.id + WHERE attrset.id ISNULL)''') AttrDB.cleanup(me) - def expiredp(me, id): + def expiry(me, id): for t, in me.select('SELECT time FROM expiry WHERE attrset = ?', [id]): - if t < time_format(): - return True - return False + return t + return None + def expiredp(me, id): + t = me.expiry(id) + if t is not None and t < time_format(): + return True + else: + return False def setexpire(me, id, when): if when != C.KEXP_FOREVER: cur = me.db.cursor() @@ -369,6 +379,7 @@ def check(db, id, sender = None, msgfile = None): keyfile = 'db/keyring' tag = 'cryptomail' dbfile = 'db/cryptomail.db' +user = None commands = {} def timecmp(x, y): @@ -383,8 +394,9 @@ def timecmp(x, y): def cmd_generate(argv): try: - opts, argv = getopt(argv, 't:c:f:', - ['expire=', 'timeout=', 'constraint=', 'format=']) + opts, argv = getopt(argv, 't:c:f:i:', + ['expire=', 'timeout=', 'constraint=', + 'info=', 'format=']) except GetoptError: return 1 kr = C.KeyFile(keyfile, C.KOPEN_WRITE) @@ -406,6 +418,8 @@ def cmd_generate(argv): map.setdefault(c, []).append(v) elif o in ('-f', '--format'): format = a + elif o in ('-i', '--info'): + map['info'] = [a] else: raise 'Barf!' if timecmp(expwhen, k.deltime) > 0: @@ -416,6 +430,8 @@ def cmd_generate(argv): a = AttrMultiMap(db) a.update(map) a['addr'] = [addr] + if user is not None: + a['user'] = [user] c = Crypto(k).encrypt(a.id) db.setexpire(a.id, expwhen) print format.replace('%', M.base32_encode(Crypto(k).encrypt(a.id)). @@ -449,18 +465,22 @@ commands['initdb'] = \ (cmd_initdb, '', """ Initialize an attribute database.""") +def getid(local): + k = C.KeyFile(keyfile, C.KOPEN_READ)[tag] + id = Crypto(k).decrypt(M.base32_decode(local)) + if id is None: + raise Reject, 'decrypt failed' + return id + def cmd_addrcheck(argv): try: opts, argv = getopt(argv, '', []) except GetoptError: return 1 local, sender = (lambda addr, sender = None, *hunoz: (addr, sender))(*argv) - k = C.KeyFile(keyfile, C.KOPEN_READ)[tag] db = CMDB(dbfile) try: - id = Crypto(k).decrypt(M.base32_decode(local)) - if id is None: - raise Reject, 'decrypt failed' + id = getid(local) addr = check(db, id, sender) except Reject, msg: print '-%s' % msg @@ -476,11 +496,12 @@ def cmd_fwaddr(argv): opts, argv = getopt(argv, '', []) except GetoptError: return 1 - local, sender = (lambda addr, sender = None, *hunoz: (addr, sender))(*argv) - k = C.KeyFile(keyfile, C.KOPEN_READ)[tag] + if len(argv) not in (1, 2): + return 1 + local, sender = (lambda addr, sender = None: (addr, sender))(*argv) db = CMDB(dbfile) try: - id = Crypto(k).decrypt(M.base32_decode(local)) + id = getid(local) if id is None: raise Reject, 'decrypt failed' addr = check(db, id, sender, stdin) @@ -490,11 +511,69 @@ def cmd_fwaddr(argv): stdin.seek(0) print addr commands['fwaddr'] = \ - (cmd_fwaddr, 'LOCAL [SENDER [IGNORED ...]]', """ + (cmd_fwaddr, 'LOCAL [SENDER]', """ Check address token LOCAL. On failure, report reason to stderr and exit 111. On success, write forwarding address to stdout and exit 0. Expects the message on standard input, as a seekable file.""") +def cmd_info(argv): + try: + opts, argv = getopt(argv, '', []) + except GetoptError: + return 1 + if len(argv) != 1: + return 1 + local = argv[0] + db = CMDB(dbfile) + try: + id = getid(local) + a = AttrMultiMap(db, id) + if user is not None and user != a.get('user', [None])[0]: + raise Reject, 'not your token' + if 'addr' not in a: + die('unknown token (expired?)') + keys = a.keys() + keys.sort() + for k in keys: + for v in a[k]: + print '%s: %s' % (k, v) + expwhen = db.expiry(id) + if expwhen: + print 'expires: %s' + else: + print 'no-expiry' + except Reject, msg: + die('invalid token') +commands['info'] = \ + (cmd_info, 'LOCAL', """ +Exaimne the address token LOCAL, and print information about it to standard +output.""") + +def cmd_revoke(argv): + try: + opts, argv = getopt(argv, '', []) + except GetoptError: + return 1 + if len(argv) != 1: + return 1 + local = argv[0] + db = CMDB(dbfile) + try: + id = getid(local) + a = AttrMultiMap(db, id) + if user is not None and user != a.get('user', [None])[0]: + raise Reject, 'not your token' + if 'addr' not in a: + die('unknown token (expired?)') + a.clear() + db.cleanup() + db.commit() + except Reject, msg: + die('invalid token') +commands['revoke'] = \ + (cmd_revoke, 'LOCAL', """ +Revoke the token LOCAL.""") + def cmd_cleanup(argv): try: opts, argv = getopt(argv, '', []) @@ -544,6 +623,7 @@ Global options: -d, --database=FILE Use FILE as the attribute database. -k, --keyring=KEYRING Use KEYRING as the keyring. -t, --tag=TAG Use TAG as the key tag. + -U, --user=USER Claim to be USER. """ cmds = commands.keys() cmds.sort() @@ -566,12 +646,12 @@ def help(): cmd_help() def main(): - global argv + global argv, user, keyfile, dbfile, tag try: opts, argv = getopt(argv[1:], - 'hvud:k:t:', + 'hvud:k:t:U:', ['help', 'version', 'usage', - 'database=', 'keyring=', 'tag=']) + 'database=', 'keyring=', 'tag=', 'user=']) except GetoptError: usage(stderr) exit(111) @@ -591,6 +671,8 @@ def main(): keyfile = a elif o in ('-t', '--tag'): tag = a + elif o in ('-U', '--user'): + user = a else: raise 'Barf!' if len(argv) < 1: -- [mdw]