Hash MAC addresses before they leave the device.
Change-Id: I9647c21a1051ad010564ba28ac1767c33a07011a
diff --git a/bouncer/authorizer.py b/bouncer/authorizer.py
index 54c1fd2..ab727a0 100755
--- a/bouncer/authorizer.py
+++ b/bouncer/authorizer.py
@@ -16,12 +16,13 @@
"""authorizer: processes Terms of Service acceptance for users."""
import logging
-import re
import subprocess
import sys
import time
+import hash_mac_addr
import options
+
import tornado.escape
import tornado.httpclient
import tornado.ioloop
@@ -57,15 +58,16 @@
class Checker(object):
"""Manage checking and polling for Terms of Service acceptance."""
- def __init__(self, mac_addr, url):
+ def __init__(self, mac_addr, hashed_mac_addr, url):
self.mac_addr = mac_addr
- self.url = url % {'mac': mac_addr}
+ self.hashed_mac_addr = hashed_mac_addr
+ self.url = url % {'mac': hashed_mac_addr}
self.tries = 0
self.callback = None
def check(self):
"""Check if a remote service knows about a device with a supplied MAC."""
- logging.info('Checking TOS for %s', self.mac_addr)
+ logging.info('Checking TOS for %s', self.hashed_mac_addr)
http_client = tornado.httpclient.HTTPClient()
response = http_client.fetch(self.url, ca_certs=opt.ca_certs)
response_obj = tornado.escape.json_decode(response.body)
@@ -77,7 +79,7 @@
if accepted_time + (opt.max_age * 86400) > time.time():
accepted = True
if self.callback: self.callback.stop()
- logging.info('TOS accepted for %s', self.mac_addr)
+ logging.info('TOS accepted for %s', self.hashed_mac_addr)
known_users[self.mac_addr] = response_obj
result = ip46tables('-A', opt.filter_chain, '-m', 'mac',
@@ -88,12 +90,13 @@
logging.error('Could not update firewall for device %s',
self.mac_addr)
else:
- logging.info('TOS accepted too long ago for %s: %r', self.mac_addr,
- accepted_time)
+ logging.info('TOS accepted too long ago for %s: %r',
+ self.hashed_mac_addr, accepted_time)
elif self.callback and self.tries > MAX_TRIES:
if not accepted:
- logging.info('TOS not accepted for %s before timeout.', self.mac_addr)
+ logging.info('TOS not accepted for %s before timeout.',
+ self.hashed_mac_addr)
self.callback.stop()
return response, accepted
@@ -108,9 +111,9 @@
cf = connection.makefile()
maybe_mac_addr = cf.readline().strip()
- if re.match('([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$', maybe_mac_addr):
- mac_addr = maybe_mac_addr.lower()
- else:
+ try:
+ mac_addr, hashed_mac_addr = hash_mac_addr.hash_mac_addr(maybe_mac_addr)
+ except ValueError:
logging.warning('can only check authorization for a MAC address.')
cf.write('{}')
return
@@ -122,7 +125,7 @@
cf.write(tornado.escape.json_encode(cached_response))
return
- checker = Checker(mac_addr, opt.url)
+ checker = Checker(mac_addr, hashed_mac_addr, opt.url)
response, accepted = checker.check()
if not accepted:
checker.poll()
diff --git a/bouncer/hash_mac_addr.py b/bouncer/hash_mac_addr.py
new file mode 100755
index 0000000..84fed1a
--- /dev/null
+++ b/bouncer/hash_mac_addr.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+"""hash_mac_addr: hash MAC addresses for privacy."""
+
+import hashlib
+import re
+import sys
+
+
+def hash_mac_addr(maybe_mac_addr):
+ if re.match('([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$', maybe_mac_addr):
+ mac_addr = maybe_mac_addr.lower()
+ else:
+ raise ValueError('%r not a MAC address', maybe_mac_addr)
+
+ return mac_addr, hashlib.sha1(mac_addr).hexdigest()
+
+if __name__ == '__main__':
+ print 'SHA1(%s): %s' % hash_mac_addr(sys.argv[1])
diff --git a/bouncer/http_bouncer.py b/bouncer/http_bouncer.py
index 3e9e138..380da4b 100755
--- a/bouncer/http_bouncer.py
+++ b/bouncer/http_bouncer.py
@@ -21,7 +21,9 @@
import sys
import urllib2
+import hash_mac_addr
import options
+
import tornado.httpclient
import tornado.ioloop
import tornado.web
@@ -66,7 +68,8 @@
else:
if self.substitute_mac:
mac = mac_for_ip(self.request.remote_ip)
- self.redirect(opt.url % {'mac': mac})
+ _, hashed_mac = hash_mac_addr.hash_mac_addr(mac)
+ self.redirect(opt.url % {'mac': hashed_mac})
if opt.unix_path:
try: