commit aa03e11328d69aaee0caa9bf0aca9cd47673ff45 Author: Ximin Luo infinity0@gmx.com Date: Sun Sep 15 18:28:51 2013 +0100
move common code into a separate flashproxy-common package --- Makefile | 7 ++- common/.gitignore | 5 ++ common/flashproxy/keys.py | 54 +++++++++++++++++++++ common/flashproxy/util.py | 52 ++++++++++++++++++++ common/setup.py | 22 +++++++++ flashproxy-client | 54 ++------------------- flashproxy-client-test | 14 +++--- flashproxy-reg-appspot | 97 ++------------------------------------ flashproxy-reg-email | 115 +++------------------------------------------ flashproxy-reg-http | 52 +------------------- flashproxy-reg-url | 64 ++----------------------- 11 files changed, 166 insertions(+), 370 deletions(-)
diff --git a/Makefile b/Makefile index 7aa71fc..62fd852 100644 --- a/Makefile +++ b/Makefile @@ -5,13 +5,14 @@ PREFIX = /usr/local BINDIR = $(PREFIX)/bin MANDIR = $(PREFIX)/share/man
-PYTHON = python +PYTHON ?= PYTHONPATH=common python export PY2EXE_TMPDIR = py2exe-tmp
CLIENT_BIN = flashproxy-client flashproxy-reg-appspot flashproxy-reg-email flashproxy-reg-http flashproxy-reg-url CLIENT_MAN = doc/flashproxy-client.1 doc/flashproxy-reg-appspot.1 doc/flashproxy-reg-email.1 doc/flashproxy-reg-http.1 doc/flashproxy-reg-url.1 CLIENT_DIST_FILES = $(CLIENT_BIN) README LICENSE ChangeLog torrc CLIENT_DIST_DOC_FILES = $(CLIENT_MAN) +CLIENT_DIST_LIB_COMMON = common/flashproxy/__init__.py common/flashproxy/keys.py common/flashproxy/util.py
all: $(CLIENT_DIST_FILES) $(CLIENT_MAN) : @@ -34,6 +35,9 @@ dist: $(CLIENT_MAN) mkdir $(DISTDIR)/doc cp -f $(CLIENT_DIST_FILES) $(DISTDIR) cp -f $(CLIENT_DIST_DOC_FILES) $(DISTDIR)/doc + test -n "$(CLIENT_DIST_LIB_COMMON)" && \ + { mkdir $(DISTDIR)/flashproxy && \ + cp -f $(CLIENT_DIST_LIB_COMMON) $(DISTDIR)/flashproxy; } || true cd dist && zip -q -r -9 $(DISTNAME).zip $(DISTNAME)
dist/$(DISTNAME).zip: $(CLIENT_DIST_FILES) @@ -51,6 +55,7 @@ $(PY2EXE_TMPDIR)/dist: $(CLIENT_BIN) dist-exe: DISTNAME := $(DISTNAME)-win32 dist-exe: CLIENT_BIN := $(PY2EXE_TMPDIR)/dist/* dist-exe: CLIENT_MAN := $(addsuffix .txt,$(CLIENT_MAN)) +dist-exe: CLIENT_DIST_LIB_COMMON := # py2exe static-links dependencies # Delegate to the "dist" target using the substitutions above. dist-exe: $(PY2EXE_TMPDIR)/dist setup.py dist
diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 0000000..b50e9ab --- /dev/null +++ b/common/.gitignore @@ -0,0 +1,5 @@ +# built by setup.py +/build +/dist +/MANIFEST +/*.egg-info diff --git a/common/flashproxy/__init__.py b/common/flashproxy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/common/flashproxy/keys.py b/common/flashproxy/keys.py new file mode 100644 index 0000000..71525c8 --- /dev/null +++ b/common/flashproxy/keys.py @@ -0,0 +1,54 @@ +# We trust no other CA certificate than this. +# +# To find the certificate to copy here, +# $ strace openssl s_client -connect FRONT_DOMAIN:443 -verify 10 -CApath /etc/ssl/certs 2>&1 | grep /etc/ssl/certs +# stat("/etc/ssl/certs/XXXXXXXX.0", {st_mode=S_IFREG|0644, st_size=YYYY, ...}) = 0 +PIN_GOOGLE_CERT = """\ +subject=/C=US/O=Equifax/OU=Equifax Secure Certificate Authority +issuer=/C=US/O=Equifax/OU=Equifax Secure Certificate Authority +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy +dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 +MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx +dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f +BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A +cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC +AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ +MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw +ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj +IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF +MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA +A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y +7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh +1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 +-----END CERTIFICATE----- +""" +# SHA-1 digest of expected public keys. Any of these is valid. See +# http://www.imperialviolet.org/2011/05/04/pinning.html for the reason behind +# hashing the public key, not the entire certificate. +PIN_GOOGLE_PUBKEY_SHA1 = ( + # https://src.chromium.org/viewvc/chrome/trunk/src/net/http/transport_security... + # kSPKIHash_Google1024 + "\x40\xc5\x40\x1d\x6f\x8c\xba\xf0\x8b\x00\xed\xef\xb1\xee\x87\xd0\x05\xb3\xb9\xcd", + # kSPKIHash_GoogleG2 + "\x43\xda\xd6\x30\xee\x53\xf8\xa9\x80\xca\x6e\xfd\x85\xf4\x6a\xa3\x79\x90\xe0\xea", +) + +# Registrations are encrypted with this public key before being emailed. Only +# the facilitator operators should have the corresponding private key. Given a +# private key in reg-email, get the public key like this: +# openssl rsa -pubout < reg-email > reg-email.pub +DEFAULT_FACILITATOR_PUBKEY_PEM = """\ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA44Mt8c599/4N2fgu6ppN +oatPW1GOgZxxObljFtEy0OWM1eHB35OOn+Kn9MxNHTRxVWwCEi0HYxWNVs2qrXxV +84LmWBz6A65d2qBlgltgLXusiXLrpwxVmJeO+GfmbF8ur0U9JSYxA20cGW/kujNg +XYDGQxO1Gvxq2lHK2LQmBpkfKEE1DMFASmIvlHDQgDj3XBb5lYeOsHZmg16UrGAq +1UH238hgJITPGLXBtwLtJkYbrATJvrEcmvI7QSm57SgYGpaB5ZdCbJL5bag5Pgt6 +M5SDDYYY4xxEPzokjFJfCQv+kcyAnzERNMQ9kR41ePTXG62bpngK5iWGeJ5XdkxG +gwIDAQAB +-----END PUBLIC KEY----- +""" diff --git a/common/flashproxy/util.py b/common/flashproxy/util.py new file mode 100644 index 0000000..47bd87a --- /dev/null +++ b/common/flashproxy/util.py @@ -0,0 +1,52 @@ +import re +import socket + +def parse_addr_spec(spec, defhost = None, defport = None): + host = None + port = None + af = 0 + m = None + # IPv6 syntax. + if not m: + m = re.match(ur'^[(.+)]:(\d*)$', spec) + if m: + host, port = m.groups() + af = socket.AF_INET6 + if not m: + m = re.match(ur'^[(.+)]$', spec) + if m: + host, = m.groups() + af = socket.AF_INET6 + # IPv4/hostname/port-only syntax. + if not m: + try: + host, port = spec.split(":", 1) + except ValueError: + host = spec + if re.match(ur'^[\d.]+$', host): + af = socket.AF_INET + else: + af = 0 + host = host or defhost + port = port or defport + if port is not None: + port = int(port) + return host, port + +def format_addr(addr): + host, port = addr + if not host: + return u":%d" % port + # Numeric IPv6 address? + try: + addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST) + af = addrs[0][0] + except socket.gaierror, e: + af = 0 + if af == socket.AF_INET6: + result = u"[%s]" % host + else: + result = "%s" % host + if port is not None: + result += u":%d" % port + return result diff --git a/common/setup.py b/common/setup.py new file mode 100755 index 0000000..44c9c3e --- /dev/null +++ b/common/setup.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +import sys + +from setuptools import setup, find_packages + +setup( + name = "flashproxy-common", + author = "dcf", + author_email = "dcf@torproject.org", + description = ("Common code for flashproxy"), + license = "BSD", + keywords = ['tor', 'flashproxy'], + + packages = find_packages(), + + version = "1.3", + + install_requires = [ + 'setuptools', + ], +) diff --git a/flashproxy-client b/flashproxy-client index cf16437..e1cfadc 100755 --- a/flashproxy-client +++ b/flashproxy-client @@ -20,6 +20,8 @@ import traceback import urllib import xml.sax.saxutils
+from flashproxy.util import parse_addr_spec, format_addr + try: from hashlib import sha1 except ImportError: @@ -147,56 +149,6 @@ def log(msg): finally: log_lock.release()
-def parse_addr_spec(spec, defhost = None, defport = None): - host = None - port = None - af = 0 - m = None - # IPv6 syntax. - if not m: - m = re.match(ur'^[(.+)]:(\d*)$', spec) - if m: - host, port = m.groups() - af = socket.AF_INET6 - if not m: - m = re.match(ur'^[(.+)]$', spec) - if m: - host, = m.groups() - af = socket.AF_INET6 - # IPv4/hostname/port-only syntax. - if not m: - try: - host, port = spec.split(":", 1) - except ValueError: - host = spec - if re.match(ur'^[\d.]+$', host): - af = socket.AF_INET - else: - af = 0 - host = host or defhost - port = port or defport - if port is not None: - port = int(port) - return host, port - -def format_addr(addr): - host, port = addr - if not host: - return u":%d" % port - # Numeric IPv6 address? - try: - addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST) - af = addrs[0][0] - except socket.gaierror, e: - af = 0 - if af == socket.AF_INET6: - result = u"[%s]" % host - else: - result = "%s" % host - if port is not None: - result += u":%d" % port - return result - def safe_format_addr(addr): return safe_str(format_addr(addr))
@@ -728,7 +680,7 @@ def forward_ports(pairs): log("%s exited with status %d." % (basename, p.returncode)) return False return True - + register_condvar = threading.Condition() # register_flag true means registration_thread_func should register at its next # opportunity. diff --git a/flashproxy-client-test b/flashproxy-client-test index 084a272..e3c6644 100755 --- a/flashproxy-client-test +++ b/flashproxy-client-test @@ -8,7 +8,7 @@ import socket import subprocess import sys import unittest - +print sys.path try: from hashlib import sha1 except ImportError: @@ -20,14 +20,14 @@ except ImportError: import imp dont_write_bytecode = sys.dont_write_bytecode sys.dont_write_bytecode = True -flashproxy = imp.load_source("flashproxy", "flashproxy-client") -parse_socks_request = flashproxy.parse_socks_request -handle_websocket_request = flashproxy.handle_websocket_request -WebSocketDecoder = flashproxy.WebSocketDecoder -WebSocketEncoder = flashproxy.WebSocketEncoder +fp_client = imp.load_source("fp_client", "flashproxy-client") +parse_socks_request = fp_client.parse_socks_request +handle_websocket_request = fp_client.handle_websocket_request +WebSocketDecoder = fp_client.WebSocketDecoder +WebSocketEncoder = fp_client.WebSocketEncoder sys.dont_write_bytecode = dont_write_bytecode del dont_write_bytecode -del flashproxy +del fp_client
LOCAL_ADDRESS = ("127.0.0.1", 40000) REMOTE_ADDRESS = ("127.0.0.1", 40001) diff --git a/flashproxy-reg-appspot b/flashproxy-reg-appspot index c84f9e7..fc49985 100755 --- a/flashproxy-reg-appspot +++ b/flashproxy-reg-appspot @@ -13,6 +13,8 @@ import tempfile import urlparse import urllib2
+from flashproxy.keys import PIN_GOOGLE_CERT, PIN_GOOGLE_PUBKEY_SHA1 +from flashproxy.util import parse_addr_spec, format_addr from hashlib import sha1
try: @@ -32,45 +34,6 @@ TARGET_DOMAIN = "fp-reg-a.appspot.com"
FLASHPROXY_REG_URL = "flashproxy-reg-url"
-# We trust no other CA certificate than this. -# -# To find the certificate to copy here, -# $ strace openssl s_client -connect FRONT_DOMAIN:443 -verify 10 -CApath /etc/ssl/certs 2>&1 | grep /etc/ssl/certs -# stat("/etc/ssl/certs/XXXXXXXX.0", {st_mode=S_IFREG|0644, st_size=YYYY, ...}) = 0 -CA_CERTS = """\ -subject=/C=US/O=Equifax/OU=Equifax Secure Certificate Authority -issuer=/C=US/O=Equifax/OU=Equifax Secure Certificate Authority ------BEGIN CERTIFICATE----- -MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV -UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy -dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 -MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx -dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B -AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f -BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A -cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC -AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ -MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm -aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw -ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj -IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF -MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA -A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y -7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh -1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 ------END CERTIFICATE----- -""" -# SHA-1 digest of expected public keys. Any of these is valid. See -# http://www.imperialviolet.org/2011/05/04/pinning.html for the reason behind -# hashing the public key, not the entire certificate. -PUBKEY_SHA1 = ( - # https://src.chromium.org/viewvc/chrome/trunk/src/net/http/transport_security... - # kSPKIHash_Google1024 - "\x40\xc5\x40\x1d\x6f\x8c\xba\xf0\x8b\x00\xed\xef\xb1\xee\x87\xd0\x05\xb3\xb9\xcd", - # kSPKIHash_GoogleG2 - "\x43\xda\xd6\x30\xee\x53\xf8\xa9\x80\xca\x6e\xfd\x85\xf4\x6a\xa3\x79\x90\xe0\xea", -) - class options(object): address_family = socket.AF_UNSPEC facilitator_pubkey_filename = None @@ -104,56 +67,6 @@ def safe_str(s): else: return s
-def parse_addr_spec(spec, defhost = None, defport = None): - host = None - port = None - af = 0 - m = None - # IPv6 syntax. - if not m: - m = re.match(ur'^[(.+)]:(\d*)$', spec) - if m: - host, port = m.groups() - af = socket.AF_INET6 - if not m: - m = re.match(ur'^[(.+)]$', spec) - if m: - host, = m.groups() - af = socket.AF_INET6 - # IPv4/hostname/port-only syntax. - if not m: - try: - host, port = spec.split(":", 1) - except ValueError: - host = spec - if re.match(ur'^[\d.]+$', host): - af = socket.AF_INET - else: - af = 0 - host = host or defhost - port = port or defport - if port is not None: - port = int(port) - return host, port - -def format_addr(addr): - host, port = addr - if not host: - return u":%d" % port - # Numeric IPv6 address? - try: - addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST) - af = addrs[0][0] - except socket.gaierror, e: - af = 0 - if af == socket.AF_INET6: - result = u"[%s]" % host - else: - result = "%s" % host - if port is not None: - result += u":%d" % port - return result - def safe_format_addr(addr): return safe_str(format_addr(addr))
@@ -225,7 +138,7 @@ class PinHTTPSConnection(httplib.HTTPSConnection): ca_certs_fd, ca_certs_path = tempfile.mkstemp(prefix="flashproxy-reg-appspot-", dir=get_state_dir(), suffix=".crt") try: - os.write(ca_certs_fd, CA_CERTS) + os.write(ca_certs_fd, PIN_GOOGLE_CERT) os.close(ca_certs_fd) ret = ctx.load_verify_locations(ca_certs_path) assert ret == 1 @@ -240,12 +153,12 @@ class PinHTTPSConnection(httplib.HTTPSConnection): for cert in self.sock.get_peer_cert_chain(): pubkey_der = cert.get_pubkey().as_der() pubkey_digest = sha1(pubkey_der).digest() - if pubkey_digest in PUBKEY_SHA1: + if pubkey_digest in PIN_GOOGLE_PUBKEY_SHA1: break found.append(pubkey_digest) else: found = "(" + ", ".join(x.encode("hex") for x in found) + ")" - expected = "(" + ", ".join(x.encode("hex") for x in PUBKEY_SHA1) + ")" + expected = "(" + ", ".join(x.encode("hex") for x in PIN_GOOGLE_PUBKEY_SHA1) + ")" raise ValueError("Public key does not match pin: got %s but expected any of %s" % (found, expected))
class PinHTTPSHandler(urllib2.HTTPSHandler): diff --git a/flashproxy-reg-email b/flashproxy-reg-email index 13ab5a5..0cb626b 100755 --- a/flashproxy-reg-email +++ b/flashproxy-reg-email @@ -10,6 +10,8 @@ import ssl import sys import tempfile
+from flashproxy.keys import PIN_GOOGLE_CERT, PIN_GOOGLE_PUBKEY_SHA1, DEFAULT_FACILITATOR_PUBKEY_PEM +from flashproxy.util import parse_addr_spec, format_addr from hashlib import sha1
try: @@ -29,59 +31,6 @@ DEFAULT_SMTP_PORT = 25 EHLO_FQDN = "[127.0.0.1]" FROM_EMAIL_ADDRESS = "nobody@localhost"
-# We trust no other CA certificate than this. -# -# To find the certificate to copy here, -# $ strace openssl s_client -connect gmail-smtp-in.l.google.com:25 -starttls smtp -verify 10 -CApath /etc/ssl/certs 2>&1 | grep /etc/ssl/certs -# stat("/etc/ssl/certs/XXXXXXXX.0", {st_mode=S_IFREG|0644, st_size=YYYY, ...}) = 0 -CA_CERTS = """\ -subject=/C=US/O=Equifax/OU=Equifax Secure Certificate Authority -issuer=/C=US/O=Equifax/OU=Equifax Secure Certificate Authority ------BEGIN CERTIFICATE----- -MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV -UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy -dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 -MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx -dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B -AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f -BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A -cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC -AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ -MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm -aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw -ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj -IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF -MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA -A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y -7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh -1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 ------END CERTIFICATE----- -""" -# SHA-1 digest of expected public keys. Any of these is valid. See -# http://www.imperialviolet.org/2011/05/04/pinning.html for the reason behind -# hashing the public key, not the entire certificate. -PUBKEY_SHA1 = ( - # https://src.chromium.org/viewvc/chrome/trunk/src/net/http/transport_security... - # kSPKIHash_Google1024 - "\x40\xc5\x40\x1d\x6f\x8c\xba\xf0\x8b\x00\xed\xef\xb1\xee\x87\xd0\x05\xb3\xb9\xcd", -) - -# Registrations are encrypted with this public key before being emailed. Only -# the facilitator operators should have the corresponding private key. Given a -# private key in reg-email, get the public key like this: -# openssl rsa -pubout < reg-email > reg-email.pub -DEFAULT_FACILITATOR_PUBKEY_PEM = """\ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA44Mt8c599/4N2fgu6ppN -oatPW1GOgZxxObljFtEy0OWM1eHB35OOn+Kn9MxNHTRxVWwCEi0HYxWNVs2qrXxV -84LmWBz6A65d2qBlgltgLXusiXLrpwxVmJeO+GfmbF8ur0U9JSYxA20cGW/kujNg -XYDGQxO1Gvxq2lHK2LQmBpkfKEE1DMFASmIvlHDQgDj3XBb5lYeOsHZmg16UrGAq -1UH238hgJITPGLXBtwLtJkYbrATJvrEcmvI7QSm57SgYGpaB5ZdCbJL5bag5Pgt6 -M5SDDYYY4xxEPzokjFJfCQv+kcyAnzERNMQ9kR41ePTXG62bpngK5iWGeJ5XdkxG -gwIDAQAB ------END PUBLIC KEY----- -""" - class options(object): remote_addr = None email_addr = None @@ -131,56 +80,6 @@ def safe_str(s): else: return s
-def parse_addr_spec(spec, defhost = None, defport = None): - host = None - port = None - af = 0 - m = None - # IPv6 syntax. - if not m: - m = re.match(ur'^[(.+)]:(\d*)$', spec) - if m: - host, port = m.groups() - af = socket.AF_INET6 - if not m: - m = re.match(ur'^[(.+)]$', spec) - if m: - host, = m.groups() - af = socket.AF_INET6 - # IPv4/hostname/port-only syntax. - if not m: - try: - host, port = spec.split(":", 1) - except ValueError: - host = spec - if re.match(ur'^[\d.]+$', host): - af = socket.AF_INET - else: - af = 0 - host = host or defhost - port = port or defport - if port is not None: - port = int(port) - return host, port - -def format_addr(addr): - host, port = addr - if not host: - return u":%d" % port - # Numeric IPv6 address? - try: - addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST) - af = addrs[0][0] - except socket.gaierror, e: - af = 0 - if af == socket.AF_INET6: - result = u"[%s]" % host - else: - result = "%s" % host - if port is not None: - result += u":%d" % port - return result - def safe_format_addr(addr): return safe_str(format_addr(addr))
@@ -265,7 +164,7 @@ try: ca_certs_fd, ca_certs_path = tempfile.mkstemp(prefix="flashproxy-reg-email-", dir=get_state_dir(), suffix=".crt") try: - os.write(ca_certs_fd, CA_CERTS) + os.write(ca_certs_fd, PIN_GOOGLE_CERT) os.close(ca_certs_fd) # We roll our own initial EHLO/STARTTLS because smtplib.SMTP.starttls # doesn't allow enough certificate validation. @@ -291,16 +190,16 @@ try: for cert in smtp.sock.get_peer_cert_chain(): pubkey_der = cert.get_pubkey().as_der() pubkey_digest = sha1(pubkey_der).digest() - if pubkey_digest in PUBKEY_SHA1: + if pubkey_digest in PIN_GOOGLE_PUBKEY_SHA1: break found.append(pubkey_digest) else: found = "(" + ", ".join(x.encode("hex") for x in found) + ")" - expected = "(" + ", ".join(x.encode("hex") for x in PUBKEY_SHA1) + ")" + expected = "(" + ", ".join(x.encode("hex") for x in PIN_GOOGLE_PUBKEY_SHA1) + ")" raise ValueError("Public key does not match pin: got %s but expected any of %s" % (found, expected))
- if options.use_certificate_pin and pubkey_digest not in PUBKEY_SHA1: - expected = "(" + ", ".join(x.encode("hex") for x in PUBKEY_SHA1) + ")" + if options.use_certificate_pin and pubkey_digest not in PIN_GOOGLE_PUBKEY_SHA1: + expected = "(" + ", ".join(x.encode("hex") for x in PIN_GOOGLE_PUBKEY_SHA1) + ")" raise ValueError("Public key does not match pin: got %s but expected any of %s" % (pubkey_digest.encode("hex"), expected))
diff --git a/flashproxy-reg-http b/flashproxy-reg-http index 975ebda..6639e57 100755 --- a/flashproxy-reg-http +++ b/flashproxy-reg-http @@ -8,6 +8,8 @@ import sys import urllib import urllib2
+from flashproxy.util import parse_addr_spec, format_addr + DEFAULT_REMOTE_ADDRESS = "" DEFAULT_REMOTE_PORT = 9000 DEFAULT_FACILITATOR_URL = "https://fp-facilitator.org/" @@ -43,56 +45,6 @@ def safe_str(s): else: return s
-def parse_addr_spec(spec, defhost = None, defport = None): - host = None - port = None - af = 0 - m = None - # IPv6 syntax. - if not m: - m = re.match(ur'^[(.+)]:(\d*)$', spec) - if m: - host, port = m.groups() - af = socket.AF_INET6 - if not m: - m = re.match(ur'^[(.+)]$', spec) - if m: - host, = m.groups() - af = socket.AF_INET6 - # IPv4/hostname/port-only syntax. - if not m: - try: - host, port = spec.split(":", 1) - except ValueError: - host = spec - if re.match(ur'^[\d.]+$', host): - af = socket.AF_INET - else: - af = 0 - host = host or defhost - port = port or defport - if port is not None: - port = int(port) - return host, port - -def format_addr(addr): - host, port = addr - if not host: - return u":%d" % port - # Numeric IPv6 address? - try: - addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST) - af = addrs[0][0] - except socket.gaierror, e: - af = 0 - if af == socket.AF_INET6: - result = u"[%s]" % host - else: - result = "%s" % host - if port is not None: - result += u":%d" % port - return result - options.facilitator_url = DEFAULT_FACILITATOR_URL options.remote_addr = (DEFAULT_REMOTE_ADDRESS, DEFAULT_REMOTE_PORT)
diff --git a/flashproxy-reg-url b/flashproxy-reg-url index b30c550..1c0b348 100755 --- a/flashproxy-reg-url +++ b/flashproxy-reg-url @@ -7,6 +7,9 @@ import socket import sys import urlparse
+from flashproxy.keys import DEFAULT_FACILITATOR_PUBKEY_PEM +from flashproxy.util import parse_addr_spec, format_addr + try: from M2Crypto import BIO, RSA except ImportError: @@ -16,17 +19,6 @@ except ImportError: DEFAULT_REMOTE_ADDRESS = None DEFAULT_REMOTE_PORT = 9000 DEFAULT_FACILITATOR_URL = "https://fp-facilitator.org/" -DEFAULT_FACILITATOR_PUBKEY_PEM = """\ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA44Mt8c599/4N2fgu6ppN -oatPW1GOgZxxObljFtEy0OWM1eHB35OOn+Kn9MxNHTRxVWwCEi0HYxWNVs2qrXxV -84LmWBz6A65d2qBlgltgLXusiXLrpwxVmJeO+GfmbF8ur0U9JSYxA20cGW/kujNg -XYDGQxO1Gvxq2lHK2LQmBpkfKEE1DMFASmIvlHDQgDj3XBb5lYeOsHZmg16UrGAq -1UH238hgJITPGLXBtwLtJkYbrATJvrEcmvI7QSm57SgYGpaB5ZdCbJL5bag5Pgt6 -M5SDDYYY4xxEPzokjFJfCQv+kcyAnzERNMQ9kR41ePTXG62bpngK5iWGeJ5XdkxG -gwIDAQAB ------END PUBLIC KEY----- -"""
class options(object): facilitator_url = None @@ -51,56 +43,6 @@ default PORT is %(port)d. "port": DEFAULT_REMOTE_PORT, }
-def parse_addr_spec(spec, defhost = None, defport = None): - host = None - port = None - af = 0 - m = None - # IPv6 syntax. - if not m: - m = re.match(ur'^[(.+)]:(\d*)$', spec) - if m: - host, port = m.groups() - af = socket.AF_INET6 - if not m: - m = re.match(ur'^[(.+)]$', spec) - if m: - host, = m.groups() - af = socket.AF_INET6 - # IPv4/hostname/port-only syntax. - if not m: - try: - host, port = spec.split(":", 1) - except ValueError: - host = spec - if re.match(ur'^[\d.]+$', host): - af = socket.AF_INET - else: - af = 0 - host = host or defhost - port = port or defport - if port is not None: - port = int(port) - return host, port - -def format_addr(addr): - host, port = addr - if not host: - return u":%d" % port - # Numeric IPv6 address? - try: - addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST) - af = addrs[0][0] - except socket.gaierror, e: - af = 0 - if af == socket.AF_INET6: - result = u"[%s]" % host - else: - result = "%s" % host - if port is not None: - result += u":%d" % port - return result - def get_facilitator_pubkey(): if options.facilitator_pubkey_filename is not None: return RSA.load_pub_key(options.facilitator_pubkey_filename)