annotate web/secure.py @ 505:ad846b9bdd10

key fingerprints are case- and whitespace-insensitive. add some comments
author Matt Johnston <matt@ucc.asn.au>
date Thu, 26 Jun 2014 23:02:23 +0800
parents 23c6cf01d237
children da769023bf08
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
505
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
1 import re
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2 import os
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3 import time
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
4 import fcntl
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
5 import hmac
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
6 import binascii
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
7 import sys
488
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
8 import hashlib
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
9
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
10 import bottle
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
11
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
12 import config
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
13
505
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
14 __all__ = ["get_csrf_blob", "check_csrf_blob", "setup_csrf", "get_user_hash",
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
15 "check_user_hash"]
488
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
16
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
17 HASH=hashlib.sha1
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
18
505
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
19 CLEAN_RE = re.compile('[^a-z0-9A-Z]')
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
20
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
21 def clean_hash(h):
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
22 return CLEAN_RE.sub('', h.lower())
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
23
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
24 def get_user_hash():
505
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
25 """
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
26 Uses the following apache config.
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
27 Needs a separate port or IP to no-certificate SSL, SNI isn't good enough.
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
28
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
29 <location /~matt/templog/set>
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
30 Require all granted
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
31 SSLVerifyClient optional_no_ca
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
32 SSLVerifyDepth 1
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
33 SSLOptions +StdEnvVars +ExportCertData +OptRenegotiate
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
34 </location>
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
35 """
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
36
489
46e327c00246 gets current params
Matt Johnston <matt@ucc.asn.au>
parents: 488
diff changeset
37 verify = bottle.request.environ.get('SSL_CLIENT_VERIFY', '')
46e327c00246 gets current params
Matt Johnston <matt@ucc.asn.au>
parents: 488
diff changeset
38 if not (verify == 'GENEROUS' or verify == 'SUCCESS'):
488
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
39 return 'FAILVERIFY'
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
40 blob = bottle.request.environ.get('SSL_CLIENT_CERT')
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
41 if not blob:
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
42 return 'NOCERT'
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
43
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
44 b64 = ''.join(l for l in blob.split('\n')
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
45 if not l.startswith('-'))
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
46
4792e9910cde watcher script
Matt Johnston <matt@ucc.asn.au>
parents: 485
diff changeset
47 return HASH(binascii.a2b_base64(b64)).hexdigest()
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
48
505
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
49 def check_user_hash(allowed_users):
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
50 current_hash = clean_hash(get_user_hash())
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
51 for a in allowed_users:
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
52 if current_hash == clean_hash(a):
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
53 return True
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
54 return False
ad846b9bdd10 key fingerprints are case- and whitespace-insensitive.
Matt Johnston <matt@ucc.asn.au>
parents: 492
diff changeset
55
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
56 def setup_csrf():
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
57 NONCE_SIZE=16
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
58 global _csrf_fd, _csrf_key
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
59 _csrf_fd = open('%s/csrf.dat' % config.DATA_PATH, 'r+')
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
60
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
61 try:
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
62 fcntl.lockf(_csrf_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
63 os.fchmod(_csrf_fd.fileno(), 0600)
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
64 _csrf_fd.write("%d-%s" % (os.getpid(), binascii.hexlify(os.urandom(NONCE_SIZE))))
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
65 _csrf_fd.flush()
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
66 _csrf_fd.seek(0)
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
67 except IOError:
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
68 pass
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
69 fcntl.lockf(_csrf_fd, fcntl.LOCK_SH)
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
70 _csrf_key = _csrf_fd.read()
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
71 # keep the lock open until we go away
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
72
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
73
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
74 def get_csrf_blob():
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
75 expiry = int(config.CSRF_TIMEOUT + time.time())
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
76 content = '%s-%s' % (get_user_hash(), expiry)
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
77 mac = hmac.new(_csrf_key, content).hexdigest()
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
78 return "%s-%s" % (content, mac)
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
79
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
80 def check_csrf_blob(blob):
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
81 toks = blob.split('-')
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
82 if len(toks) != 3:
492
23c6cf01d237 working kinda
Matt Johnston <matt@ucc.asn.au>
parents: 489
diff changeset
83 print>>sys.stderr, "wrong toks"
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
84 return False
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
85
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
86 user, expiry, mac = toks
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
87 if user != get_user_hash():
492
23c6cf01d237 working kinda
Matt Johnston <matt@ucc.asn.au>
parents: 489
diff changeset
88 print>>sys.stderr, "wrong user"
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
89 return False
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
90
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
91 try:
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
92 exp = int(expiry)
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
93 except ValueError:
492
23c6cf01d237 working kinda
Matt Johnston <matt@ucc.asn.au>
parents: 489
diff changeset
94 print>>sys.stderr, "failed exp"
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
95 return False
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
96
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
97 if exp < 1000000000:
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
98 return False
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
99
492
23c6cf01d237 working kinda
Matt Johnston <matt@ucc.asn.au>
parents: 489
diff changeset
100 if exp < time.time():
23c6cf01d237 working kinda
Matt Johnston <matt@ucc.asn.au>
parents: 489
diff changeset
101 print>>sys.stderr, "expired %d %d" % (exp, time.time())
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
102 return False
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
103
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
104 check_content = "%s-%s" % (user, expiry)
492
23c6cf01d237 working kinda
Matt Johnston <matt@ucc.asn.au>
parents: 489
diff changeset
105 check_mac = hmac.new(_csrf_key, check_content).hexdigest()
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
106 if mac == check_mac:
492
23c6cf01d237 working kinda
Matt Johnston <matt@ucc.asn.au>
parents: 489
diff changeset
107 print>>sys.stderr, "good hmac"
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
108 return True
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
109
492
23c6cf01d237 working kinda
Matt Johnston <matt@ucc.asn.au>
parents: 489
diff changeset
110 print>>sys.stderr, "fail"
485
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
111 return False
d68af9e84485 working
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
112