chiark / gitweb /
cryptomail: Implement info and revoke commands.
authorMark Wooding <mdw@distorted.org.uk>
Tue, 21 Mar 2006 11:52:41 +0000 (11:52 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Tue, 21 Mar 2006 11:55:07 +0000 (11:55 +0000)
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

index 27beea296f557688db5dc7921d69bd18ab944bb6..3a24d042cf639c3b1506aecaffb9225d0229fdbc 100755 (executable)
@@ -221,12 +221,22 @@ class CMDB (AttrDB):
                            (SELECT attrset FROM expiry WHERE time < ?)''',
                 [now])
     cur.execute('DELETE FROM expiry WHERE time < ?', [now])
                            (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)
     AttrDB.cleanup(me)
-  def expiredp(me, id):
+  def expiry(me, id):
     for t, in me.select('SELECT time FROM expiry WHERE attrset = ?', [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()
   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'
 keyfile = 'db/keyring'
 tag = 'cryptomail'
 dbfile = 'db/cryptomail.db'
+user = None
 commands = {}
 
 def timecmp(x, y):
 commands = {}
 
 def timecmp(x, y):
@@ -383,8 +394,9 @@ def timecmp(x, y):
 
 def cmd_generate(argv):
   try:
 
 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)
   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
       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:
     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]
   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)).
   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.""")
 
   (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)
 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:
   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
     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
     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:
   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)
     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'] = \
   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.""")
 
 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, '', [])
 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.
   -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()
 """
     cmds = commands.keys()
     cmds.sort()
@@ -566,12 +646,12 @@ def help():
   cmd_help()  
 
 def main():
   cmd_help()  
 
 def main():
-  global argv
+  global argv, user, keyfile, dbfile, tag
   try:
     opts, argv = getopt(argv[1:],
   try:
     opts, argv = getopt(argv[1:],
-                        'hvud:k:t:',
+                        'hvud:k:t:U:',
                         ['help', 'version', 'usage',
                         ['help', 'version', 'usage',
-                         'database=', 'keyring=', 'tag='])
+                         'database=', 'keyring=', 'tag=', 'user='])
   except GetoptError:
     usage(stderr)
     exit(111)
   except GetoptError:
     usage(stderr)
     exit(111)
@@ -591,6 +671,8 @@ def main():
       keyfile = a
     elif o in ('-t', '--tag'):
       tag = a
       keyfile = a
     elif o in ('-t', '--tag'):
       tag = a
+    elif o in ('-U', '--user'):
+      user = a
     else:
       raise 'Barf!'
   if len(argv) < 1:
     else:
       raise 'Barf!'
   if len(argv) < 1: