[tor-commits] [flashproxy/master] move common functionality and command-line options into flashproxy-common
infinity0 at torproject.org
infinity0 at torproject.org
Mon Jul 7 08:55:34 UTC 2014
commit badb39f9c13497a7887bac4ac24210ec12be9e39
Author: Ximin Luo <infinity0 at torproject.org>
Date: Thu Feb 13 16:45:09 2014 +0000
move common functionality and command-line options into flashproxy-common
- move keys.DEFAULT_FACILITATOR_PUBKEY_PEM into new reg module to be with other default-facilitator data
---
flashproxy-client | 37 +++--------------
flashproxy-reg-appspot | 97 ++++++---------------------------------------
flashproxy-reg-email | 103 +++++++-----------------------------------------
flashproxy-reg-http | 69 ++++----------------------------
flashproxy-reg-url | 65 ++++--------------------------
flashproxy/keys.py | 47 ++++++++++++++--------
flashproxy/reg.py | 59 ++++++++++++++++++++++++++-
flashproxy/util.py | 38 ++++++++++++++++++
8 files changed, 177 insertions(+), 338 deletions(-)
diff --git a/flashproxy-client b/flashproxy-client
index 9e198a4..b69d1bf 100755
--- a/flashproxy-client
+++ b/flashproxy-client
@@ -8,6 +8,7 @@ import BaseHTTPServer
import array
import base64
import cStringIO
+import flashproxy
import os
import os.path
import select
@@ -19,7 +20,8 @@ import threading
import time
import traceback
-from flashproxy.util import parse_addr_spec, addr_family, format_addr
+from flashproxy.util import parse_addr_spec, addr_family, format_addr, safe_str, safe_format_addr
+from flashproxy.reg import DEFAULT_TRANSPORT
from hashlib import sha1
@@ -35,7 +37,6 @@ DEFAULT_LOCAL_PORT_EXTERNAL = 9001
DEFAULT_REMOTE_PORT = 9000
DEFAULT_REGISTER_METHODS = ["appspot", "email", "http"]
DEFAULT_PORT_FORWARDING_HELPER = "tor-fw-helper"
-DEFAULT_TRANSPORT = "websocket"
# We will re-register if we have fewer than this many waiting proxies. The
# facilitator may choose to ignore our requests.
@@ -71,22 +72,12 @@ class options(object):
facilitator_url = None
facilitator_pubkey_filename = None
-def safe_str(s):
- """Return "[scrubbed]" if options.safe_logging is true, and s otherwise."""
- if options.safe_logging:
- return "[scrubbed]"
- else:
- return s
-
log_lock = threading.Lock()
def log(msg):
with log_lock:
print >> options.log_file, (u"%s %s" % (time.strftime(LOG_DATE_FORMAT), msg)).encode("UTF-8")
options.log_file.flush()
-def safe_format_addr(addr):
- return safe_str(format_addr(addr))
-
def format_sockaddr(sockaddr):
host, port = socket.getnameinfo(sockaddr, socket.NI_NUMERICHOST | socket.NI_NUMERICSERV)
port = int(port)
@@ -1036,19 +1027,8 @@ The -4, -6, --unsafe-logging, --transport and --facilitator-pubkey options are
propagated to the child registration helpers. For backwards compatilibility,
the --facilitator option is also propagated to the http registration helper.
If you need to pass more options, use TODO #9976.""")
- # common opts
- parser.add_argument("-4", help="name lookups use only IPv4.",
- action="store_const", const=socket.AF_INET, dest="address_family")
- parser.add_argument("-6", help="name lookups use only IPv6.",
- action="store_const", const=socket.AF_INET6, dest="address_family")
- parser.add_argument("--unsafe-logging", help="don't scrub IP addresses and "
- "other sensitive information from logs.", action="store_true")
- parser.add_argument("--facilitator-pubkey", help="encrypt registrations to "
- "the given PEM-formatted public key file (default built-in).",
- metavar='FILENAME')
- parser.add_argument("--transport",
- help="register using the given transport, default %(default)s.",
- default=DEFAULT_TRANSPORT)
+ flashproxy.util.add_module_opts(parser)
+ flashproxy.reg.add_module_opts(parser)
parser.add_argument("-f", "--facilitator", metavar="URL",
help="register with the facilitator at this URL, default %(default)s. "
"This is passed to the http registration ONLY.")
@@ -1090,12 +1070,7 @@ If you need to pass more options, use TODO #9976.""")
ns = parser.parse_args(sys.argv[1:])
# set registration options
- options.address_family = ns.address_family or socket.AF_UNSPEC
- if options.address_family != socket.AF_UNSPEC:
- getaddrinfo = socket.getaddrinfo
- def getaddrinfo_replacement(host, port, family, *args, **kwargs):
- return getaddrinfo(host, port, options.address_family, *args, **kwargs)
- socket.getaddrinfo = getaddrinfo_replacement
+ options.address_family = ns.address_family
options.transport = ns.transport
options.safe_logging = not ns.unsafe_logging
options.facilitator_url = ns.facilitator
diff --git a/flashproxy-reg-appspot b/flashproxy-reg-appspot
index f0e4714..42aef97 100755
--- a/flashproxy-reg-appspot
+++ b/flashproxy-reg-appspot
@@ -2,16 +2,16 @@
"""Register with a facilitator through Google App Engine."""
import argparse
+import flashproxy
import httplib
-import os
import socket
-import subprocess
import sys
import urlparse
import urllib2
from flashproxy.keys import PIN_GOOGLE_CA_CERT, PIN_GOOGLE_PUBKEY_SHA1, check_certificate_pin, ensure_M2Crypto, temp_cert
-from flashproxy.util import parse_addr_spec, format_addr
+from flashproxy.reg import build_reg_b64enc
+from flashproxy.util import parse_addr_spec, safe_str, safe_format_addr
try:
from M2Crypto import SSL
@@ -19,54 +19,11 @@ except ImportError:
# Defer the error reporting so that --help works even without M2Crypto.
pass
-DEFAULT_REMOTE = ("", 9000)
-DEFAULT_TRANSPORT = "websocket"
-
# The domain to which requests appear to go.
FRONT_DOMAIN = "www.google.com"
# The value of the Host header within requests.
TARGET_DOMAIN = "fp-reg-a.appspot.com"
-FLASHPROXY_REG_URL = "flashproxy-reg-url"
-
-class options(object):
- address_family = socket.AF_UNSPEC
- use_certificate_pin = True
- facilitator_pubkey_filename = None
- transport = DEFAULT_TRANSPORT
- safe_logging = True
-
-def safe_str(s):
- """Return "[scrubbed]" if options.safe_logging is true, and s otherwise."""
- if options.safe_logging:
- return "[scrubbed]"
- else:
- return s
-
-def safe_format_addr(addr):
- return safe_str(format_addr(addr))
-
-def generate_url(addr):
- if getattr(sys, "frozen", False):
- script_dir = os.path.dirname(sys.executable)
- else:
- script_dir = sys.path[0]
- if not script_dir:
- # Maybe the script was read from stdin; in any case don't guess at the directory.
- raise ValueError("Can't find executable directory for registration helpers")
- command = [os.path.join(script_dir, FLASHPROXY_REG_URL)]
- command += ["-f", urlparse.urlunparse(("https", FRONT_DOMAIN, "/", "", "", ""))]
- if options.transport is not None:
- command += ["--transport", options.transport]
- if options.facilitator_pubkey_filename is not None:
- command += ["--facilitator-pubkey", options.facilitator_pubkey_filename]
- command.append(format_addr(addr))
- p = subprocess.Popen(command, stdout=subprocess.PIPE)
- stdout, stderr = p.communicate()
- if p.returncode != 0:
- raise ValueError("%s exited with status %d" % (FLASHPROXY_REG_URL, p.returncode))
- return stdout.strip()
-
# Like socket.create_connection in that it tries resolving different address
# families, but doesn't connect the socket.
def create_socket(address, timeout = None):
@@ -105,8 +62,7 @@ class PinHTTPSConnection(httplib.HTTPSConnection):
self.sock = SSL.Connection(ctx, sock)
self.sock.connect((self.host, self.port))
- if options.use_certificate_pin:
- check_certificate_pin(self.sock, PIN_GOOGLE_PUBKEY_SHA1)
+ check_certificate_pin(self.sock, PIN_GOOGLE_PUBKEY_SHA1)
class PinHTTPSHandler(urllib2.HTTPSHandler):
def https_open(self, req):
@@ -130,40 +86,12 @@ parser = argparse.ArgumentParser(
description="Register with a facilitator through a Google App Engine app. "
"If only the external port is given, the remote server guesses our "
"external address.")
-# common opts
-parser.add_argument("-4", help="name lookups use only IPv4.",
- action="store_const", const=socket.AF_INET, dest="address_family")
-parser.add_argument("-6", help="name lookups use only IPv6.",
- action="store_const", const=socket.AF_INET6, dest="address_family")
-parser.add_argument("--unsafe-logging", help="don't scrub IP addresses and "
- "other sensitive information from logs.", action="store_true")
-parser.add_argument("--disable-pin", help="disable all certificate pinning "
- "checks", action="store_true",)
-parser.add_argument("--facilitator-pubkey", help="encrypt registrations to "
- "the given PEM-formatted public key file (default built-in).",
- metavar='FILENAME')
-parser.add_argument("--transport",
- help="register using the given transport, default %(default)s.",
- default=DEFAULT_TRANSPORT)
-# common args
-parser.add_argument("remote_addr",
- help="remote to register, default %s - the external IP address is guessed."
- % format_addr(DEFAULT_REMOTE),
- metavar="REMOTE:PORT", default="", nargs="?",
- type=lambda x: parse_addr_spec(x, *DEFAULT_REMOTE))
-
-ns = parser.parse_args(sys.argv[1:])
-options.address_family = ns.address_family or socket.AF_UNSPEC
-if options.address_family != socket.AF_UNSPEC:
- getaddrinfo = socket.getaddrinfo
- def getaddrinfo_replacement(host, port, family, *args, **kwargs):
- return getaddrinfo(host, port, options.address_family, *args, **kwargs)
- socket.getaddrinfo = getaddrinfo_replacement
-options.safe_logging = not ns.unsafe_logging
-options.use_certificate_pin = not ns.disable_pin
-options.facilitator_pubkey_filename = ns.facilitator_pubkey
-options.transport = ns.transport
-remote_addr = ns.remote_addr
+flashproxy.util.add_module_opts(parser)
+flashproxy.keys.add_module_opts(parser)
+flashproxy.reg.add_registration_args(parser)
+
+options = parser.parse_args(sys.argv[1:])
+remote_addr = options.remote_addr
ensure_M2Crypto()
@@ -186,9 +114,10 @@ if not remote_addr[0]:
sys.exit(1)
try:
- url = generate_url(remote_addr)
+ reg = build_reg_b64enc(remote_addr, options.transport, urlsafe=True)
+ url = urlparse.urljoin(urlparse.urlunparse(("https", FRONT_DOMAIN, "/", "", "", "")), "reg/" + reg)
except Exception, e:
- print >> sys.stderr, "Error running %s: %s" % (FLASHPROXY_REG_URL, str(e))
+ print >> sys.stderr, "Error generating URL: %s" % str(e)
sys.exit(1)
try:
diff --git a/flashproxy-reg-email b/flashproxy-reg-email
index a151efd..ddfc9fa 100755
--- a/flashproxy-reg-email
+++ b/flashproxy-reg-email
@@ -2,66 +2,30 @@
"""Register with a facilitator using the email method."""
import argparse
+import flashproxy
import os
import re
import smtplib
-import socket
import sys
-import urllib
-from flashproxy.keys import PIN_GOOGLE_CA_CERT, PIN_GOOGLE_PUBKEY_SHA1, DEFAULT_FACILITATOR_PUBKEY_PEM, check_certificate_pin, ensure_M2Crypto, temp_cert
-from flashproxy.util import parse_addr_spec, format_addr
+from flashproxy.keys import PIN_GOOGLE_CA_CERT, PIN_GOOGLE_PUBKEY_SHA1, check_certificate_pin, ensure_M2Crypto, temp_cert
+from flashproxy.reg import build_reg_b64enc
+from flashproxy.util import parse_addr_spec, format_addr, safe_format_addr
try:
- from M2Crypto import BIO, RSA, SSL
+ from M2Crypto import SSL
except ImportError:
# Defer the error reporting so that --help works even without M2Crypto.
pass
-DEFAULT_REMOTE = ("", 9000)
DEFAULT_EMAIL_ADDRESS = "flashproxyreg.a at gmail.com"
# dig MX gmail.com
DEFAULT_SMTP = ("gmail-smtp-in.l.google.com", 25)
-DEFAULT_TRANSPORT = "websocket"
# Use this to prevent Python smtplib from guessing and leaking our hostname.
EHLO_FQDN = "[127.0.0.1]"
FROM_EMAIL_ADDRESS = "nobody at localhost"
-class options(object):
- remote_addr = None
-
- address_family = socket.AF_UNSPEC
- debug = False
- use_certificate_pin = True
- email_addr = None
- facilitator_pubkey_filename = None
- smtp_addr = None
- transport = DEFAULT_TRANSPORT
- safe_logging = True
-
-def safe_str(s):
- """Return "[scrubbed]" if options.safe_logging is true, and s otherwise."""
- if options.safe_logging:
- return "[scrubbed]"
- else:
- return s
-
-def safe_format_addr(addr):
- return safe_str(format_addr(addr))
-
-def build_reg(addr, transport):
- return urllib.urlencode((
- ("client", format_addr(addr)),
- ("client-transport", transport),
- ))
-
-def get_facilitator_pubkey():
- if options.facilitator_pubkey_filename is not None:
- return RSA.load_pub_key(options.facilitator_pubkey_filename)
- else:
- return RSA.load_pub_key_bio(BIO.MemoryBuffer(DEFAULT_FACILITATOR_PUBKEY_PEM))
-
parser = argparse.ArgumentParser(
usage="%(prog)s [OPTIONS] [REMOTE][:PORT]",
description="Register with a flash proxy facilitator through email. Makes "
@@ -71,27 +35,9 @@ parser = argparse.ArgumentParser(
epilog="Using an SMTP server or email address other than the defaults will "
"not work unless you have made special arrangements to connect them to a "
"facilitator.")
-# common opts
-parser.add_argument("-4", help="name lookups use only IPv4.",
- action="store_const", const=socket.AF_INET, dest="address_family")
-parser.add_argument("-6", help="name lookups use only IPv6.",
- action="store_const", const=socket.AF_INET6, dest="address_family")
-parser.add_argument("--unsafe-logging", help="don't scrub IP addresses and "
- "other sensitive information from logs.", action="store_true")
-parser.add_argument("--disable-pin", help="disable all certificate pinning "
- "checks", action="store_true",)
-parser.add_argument("--facilitator-pubkey", help="encrypt registrations to "
- "the given PEM-formatted public key file (default built-in).",
- metavar='FILENAME')
-parser.add_argument("--transport",
- help="register using the given transport, default %(default)s.",
- default=DEFAULT_TRANSPORT)
-# common args
-parser.add_argument("remote_addr",
- help="remote to register, default %s - the external IP address is guessed."
- % format_addr(DEFAULT_REMOTE),
- metavar="REMOTE:PORT", default="", nargs="?",
- type=lambda x: parse_addr_spec(x, *DEFAULT_REMOTE))
+flashproxy.util.add_module_opts(parser)
+flashproxy.keys.add_module_opts(parser)
+flashproxy.reg.add_registration_args(parser)
# specific opts
parser.add_argument("-e", "--email", metavar="ADDRESS",
help="send mail to ADDRESS, default %(default)s.",
@@ -103,26 +49,11 @@ parser.add_argument("-d", "--debug",
help="enable debugging output (Python smtplib messages).",
action="store_true")
-ns = parser.parse_args(sys.argv[1:])
-options.address_family = ns.address_family or socket.AF_UNSPEC
-if options.address_family != socket.AF_UNSPEC:
- getaddrinfo = socket.getaddrinfo
- def getaddrinfo_replacement(host, port, family, *args, **kwargs):
- return getaddrinfo(host, port, options.address_family, *args, **kwargs)
- socket.getaddrinfo = getaddrinfo_replacement
-options.safe_logging = not ns.unsafe_logging
-options.use_certificate_pin = not ns.disable_pin
-options.facilitator_pubkey_filename = ns.facilitator_pubkey
-options.transport = ns.transport
-options.remote_addr = ns.remote_addr
-# specific parsing
-options.email_addr = ns.email
-options.smtp_addr = ns.smtp
-options.debug = ns.debug
+options = parser.parse_args(sys.argv[1:])
ensure_M2Crypto()
-smtp = smtplib.SMTP(options.smtp_addr[0], options.smtp_addr[1], EHLO_FQDN)
+smtp = smtplib.SMTP(options.smtp[0], options.smtp[1], EHLO_FQDN)
if options.debug:
smtp.set_debuglevel(1)
@@ -149,8 +80,7 @@ try:
smtp.sock.connect_ssl()
smtp.file = smtp.sock.makefile()
- if options.use_certificate_pin:
- check_certificate_pin(smtp.sock, PIN_GOOGLE_PUBKEY_SHA1)
+ check_certificate_pin(smtp.sock, PIN_GOOGLE_PUBKEY_SHA1)
smtp.ehlo(EHLO_FQDN)
if not options.remote_addr[0]:
@@ -164,21 +94,18 @@ try:
spec = "[" + spec + "]"
options.remote_addr = parse_addr_spec(spec, *options.remote_addr)
- body_plain = build_reg(options.remote_addr, options.transport)
- rsa = get_facilitator_pubkey()
- body_crypt = rsa.public_encrypt(body_plain, RSA.pkcs1_oaep_padding)
- body = body_crypt.encode("base64")
+ body = build_reg_b64enc(options.remote_addr, options.transport)
# Add a random subject to keep Gmail from threading everything.
rand_string = os.urandom(5).encode("hex")
- smtp.sendmail(options.email_addr, options.email_addr, """\
+ smtp.sendmail(options.email, options.email, """\
To: %(to_addr)s\r
From: %(from_addr)s\r
Subject: client reg %(rand_string)s\r
\r
%(body)s
""" % {
- "to_addr": options.email_addr,
+ "to_addr": options.email,
"from_addr": FROM_EMAIL_ADDRESS,
"rand_string": rand_string,
"body": body,
@@ -188,4 +115,4 @@ except Exception, e:
print >> sys.stderr, "Failed to register: %s" % str(e)
sys.exit(1)
-print "Registered \"%s\" with %s." % (safe_format_addr(options.remote_addr), options.email_addr)
+print "Registered \"%s\" with %s." % (safe_format_addr(options.remote_addr), options.email)
diff --git a/flashproxy-reg-http b/flashproxy-reg-http
index a3ad246..debd2d1 100755
--- a/flashproxy-reg-http
+++ b/flashproxy-reg-http
@@ -2,82 +2,29 @@
"""Register with a facilitator using the HTTP method."""
import argparse
-import socket
+import flashproxy
import sys
-import urllib
import urllib2
-from flashproxy.util import parse_addr_spec, format_addr
-
-DEFAULT_REMOTE = ("", 9000)
-DEFAULT_FACILITATOR_URL = "https://fp-facilitator.org/"
-DEFAULT_TRANSPORT = "websocket"
-
-class options(object):
- remote_addr = None
-
- address_family = socket.AF_UNSPEC
- facilitator_url = None
- transport = DEFAULT_TRANSPORT
- safe_logging = True
-
-def safe_str(s):
- """Return "[scrubbed]" if options.safe_logging is true, and s otherwise."""
- if options.safe_logging:
- return "[scrubbed]"
- else:
- return s
-
-def safe_format_addr(addr):
- return safe_str(format_addr(addr))
-
-def build_reg(addr, transport):
- return urllib.urlencode((
- ("client", format_addr(addr)),
- ("client-transport", transport),
- ))
+from flashproxy.util import safe_format_addr
+from flashproxy.reg import DEFAULT_FACILITATOR_URL, build_reg
parser = argparse.ArgumentParser(
usage="%(prog)s [OPTIONS] [REMOTE][:PORT]",
description="Register with a flash proxy facilitator using an HTTP POST. "
"If only the external port is given, the remote server guesses our "
"external address.")
-# common opts
-parser.add_argument("-4", help="name lookups use only IPv4.",
- action="store_const", const=socket.AF_INET, dest="address_family")
-parser.add_argument("-6", help="name lookups use only IPv6.",
- action="store_const", const=socket.AF_INET6, dest="address_family")
-parser.add_argument("--unsafe-logging", help="don't scrub IP addresses and "
- "other sensitive information from logs.", action="store_true")
-parser.add_argument("--transport",
- help="register using the given transport, default %(default)s.",
- default=DEFAULT_TRANSPORT)
-# common args
-parser.add_argument("remote_addr",
- help="remote to register, default %s - the external IP address is guessed."
- % format_addr(DEFAULT_REMOTE),
- metavar="REMOTE:PORT", default="", nargs="?",
- type=lambda x: parse_addr_spec(x, *DEFAULT_REMOTE))
-# specific opts
+flashproxy.util.add_module_opts(parser)
+flashproxy.reg.add_registration_args(parser, ignore_pubkey=True)
parser.add_argument("-f", "--facilitator", metavar="URL",
help="register with the given facilitator, default %(default)s.",
default=DEFAULT_FACILITATOR_URL)
-ns = parser.parse_args(sys.argv[1:])
-options.address_family = ns.address_family or socket.AF_UNSPEC
-if options.address_family != socket.AF_UNSPEC:
- getaddrinfo = socket.getaddrinfo
- def getaddrinfo_replacement(host, port, family, *args, **kwargs):
- return getaddrinfo(host, port, options.address_family, *args, **kwargs)
- socket.getaddrinfo = getaddrinfo_replacement
-options.safe_logging = not ns.unsafe_logging
-options.transport = ns.transport
-options.remote_addr = ns.remote_addr
-options.facilitator_url = ns.facilitator
+options = parser.parse_args(sys.argv[1:])
body = build_reg(options.remote_addr, options.transport)
try:
- http = urllib2.urlopen(options.facilitator_url, body, 10)
+ http = urllib2.urlopen(options.facilitator, body, 10)
except urllib2.HTTPError, e:
print >> sys.stderr, "Status code was %d, not 200" % e.code
sys.exit(1)
@@ -89,4 +36,4 @@ except Exception, e:
sys.exit(1)
http.close()
-print "Registered \"%s\" with %s." % (safe_format_addr(options.remote_addr), options.facilitator_url)
+print "Registered \"%s\" with %s." % (safe_format_addr(options.remote_addr), options.facilitator)
diff --git a/flashproxy-reg-url b/flashproxy-reg-url
index e8789d5..e73b035 100755
--- a/flashproxy-reg-url
+++ b/flashproxy-reg-url
@@ -2,78 +2,29 @@
"""Register with a facilitator using an indirect URL."""
import argparse
-import base64
+import flashproxy
import sys
-import urllib
import urlparse
-from flashproxy.keys import DEFAULT_FACILITATOR_PUBKEY_PEM, ensure_M2Crypto
-from flashproxy.util import parse_addr_spec, format_addr
-
-try:
- from M2Crypto import BIO, RSA
-except ImportError:
- # Defer the error reporting so that --help works even without M2Crypto.
- pass
-
-DEFAULT_REMOTE = ("", 9000)
-DEFAULT_FACILITATOR_URL = "https://fp-facilitator.org/"
-DEFAULT_TRANSPORT = "websocket"
-
-class options(object):
- facilitator_url = None
- facilitator_pubkey_filename = None
- transport = DEFAULT_TRANSPORT
-
-def build_reg(addr, transport):
- return urllib.urlencode((
- ("client", format_addr(addr)),
- ("client-transport", transport),
- ))
-
-def get_facilitator_pubkey():
- if options.facilitator_pubkey_filename is not None:
- return RSA.load_pub_key(options.facilitator_pubkey_filename)
- else:
- return RSA.load_pub_key_bio(BIO.MemoryBuffer(DEFAULT_FACILITATOR_PUBKEY_PEM))
+from flashproxy.keys import ensure_M2Crypto
+from flashproxy.reg import DEFAULT_FACILITATOR_URL, build_reg_b64enc
parser = argparse.ArgumentParser(
usage="%(prog)s [OPTIONS] REMOTE[:PORT]",
description="Print a URL, which, when retrieved, will cause the input "
"client address to be registered with the flash proxy facilitator.")
-# common opts
-parser.add_argument("--facilitator-pubkey", help="encrypt registrations to "
- "the given PEM-formatted public key file (default built-in).",
- metavar='FILENAME')
-parser.add_argument("--transport",
- help="register using the given transport, default %(default)s.",
- default=DEFAULT_TRANSPORT)
-# common args
-parser.add_argument("remote_addr",
- help="remote to register, default %s - the external IP address is guessed."
- % format_addr(DEFAULT_REMOTE),
- metavar="REMOTE:PORT", default="", nargs="?",
- type=lambda x: parse_addr_spec(x, *DEFAULT_REMOTE))
-# specific opts
+flashproxy.reg.add_registration_args(parser)
parser.add_argument("-f", "--facilitator", metavar="URL",
help="register with the given facilitator, default %(default)s.",
default=DEFAULT_FACILITATOR_URL)
-ns = parser.parse_args(sys.argv[1:])
-options.facilitator_pubkey_filename = ns.facilitator_pubkey
-options.transport = ns.transport
-remote_addr = ns.remote_addr
-options.facilitator_url = ns.facilitator
+options = parser.parse_args(sys.argv[1:])
ensure_M2Crypto()
-if not ns.remote_addr[0]:
+if not options.remote_addr[0]:
print >> sys.stderr, "An IP address (not just a port) is required."
sys.exit(1)
-reg_plain = build_reg(remote_addr, options.transport)
-rsa = get_facilitator_pubkey()
-reg_crypt = rsa.public_encrypt(reg_plain, RSA.pkcs1_oaep_padding)
-reg = base64.urlsafe_b64encode(reg_crypt)
-
-print urlparse.urljoin(options.facilitator_url, "reg/" + reg)
+reg = build_reg_b64enc(options.remote_addr, options.transport, urlsafe=True)
+print urlparse.urljoin(options.facilitator, "reg/" + reg)
diff --git a/flashproxy/keys.py b/flashproxy/keys.py
index 1365f07..8c60dc0 100644
--- a/flashproxy/keys.py
+++ b/flashproxy/keys.py
@@ -1,15 +1,32 @@
+import base64
import errno
import os
+import sys
import tempfile
from hashlib import sha1
try:
import M2Crypto
+ from M2Crypto import BIO, RSA
except ImportError:
# Defer the error so that the main program gets a chance to print help text
M2Crypto = None
+class options(object):
+ disable_pin = True
+
+def add_module_opts(parser):
+ parser.add_argument("--disable-pin", help="disable all certificate pinning "
+ "checks", action="store_true",)
+
+ old_parse = parser.parse_args
+ def parse_args(namespace):
+ options.disable_pin = namespace.disable_pin
+ return namespace
+ parser.parse_args = lambda *a, **kw: parse_args(old_parse(*a, **kw))
+
+
# We trust no other CA certificate than this.
#
# To find the certificate to copy here,
@@ -49,23 +66,8 @@ PIN_GOOGLE_PUBKEY_SHA1 = (
"\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-----
-"""
-
def check_certificate_pin(sock, cert_pubkey):
+ if options.disable_pin: return
found = []
for cert in sock.get_peer_cert_chain():
pubkey_der = cert.get_pubkey().as_der()
@@ -105,6 +107,19 @@ class temp_cert(object):
def __exit__(self, type, value, traceback):
os.unlink(self.path)
+def get_pubkey(defaultkeybytes, overridefn=None):
+ if overridefn is not None:
+ return RSA.load_pub_key(overridefn)
+ else:
+ return RSA.load_pub_key_bio(BIO.MemoryBuffer(defaultkeybytes))
+
+def pubkey_b64enc(plaintext, pubkey, urlsafe=False):
+ ciphertext = pubkey.public_encrypt(plaintext, RSA.pkcs1_oaep_padding)
+ if urlsafe:
+ return base64.urlsafe_b64encode(ciphertext)
+ else:
+ return ciphertext.encode("base64")
+
def ensure_M2Crypto():
if M2Crypto is None:
print >> sys.stderr, """\
diff --git a/flashproxy/reg.py b/flashproxy/reg.py
index 0551f06..bc292dc 100644
--- a/flashproxy/reg.py
+++ b/flashproxy/reg.py
@@ -1,6 +1,63 @@
+import urllib
from collections import namedtuple
-from flashproxy.util import parse_addr_spec
+from flashproxy.keys import get_pubkey, pubkey_b64enc
+from flashproxy.util import parse_addr_spec, format_addr
+
+DEFAULT_REMOTE = ("", 9000)
+DEFAULT_FACILITATOR_URL = "https://fp-facilitator.org/"
+DEFAULT_TRANSPORT = "websocket"
+# Default facilitator pubkey owned by the operator of DEFAULT_FACILITATOR_URL
+DEFAULT_FACILITATOR_PUBKEY_PEM = """\
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA44Mt8c599/4N2fgu6ppN
+oatPW1GOgZxxObljFtEy0OWM1eHB35OOn+Kn9MxNHTRxVWwCEi0HYxWNVs2qrXxV
+84LmWBz6A65d2qBlgltgLXusiXLrpwxVmJeO+GfmbF8ur0U9JSYxA20cGW/kujNg
+XYDGQxO1Gvxq2lHK2LQmBpkfKEE1DMFASmIvlHDQgDj3XBb5lYeOsHZmg16UrGAq
+1UH238hgJITPGLXBtwLtJkYbrATJvrEcmvI7QSm57SgYGpaB5ZdCbJL5bag5Pgt6
+M5SDDYYY4xxEPzokjFJfCQv+kcyAnzERNMQ9kR41ePTXG62bpngK5iWGeJ5XdkxG
+gwIDAQAB
+-----END PUBLIC KEY-----
+"""
+_OPTION_IGNORED = "ignored; for compatibility with other methods"
+
+class options(object):
+ transport = DEFAULT_TRANSPORT
+ facilitator_pubkey = None
+
+def add_module_opts(parser, ignore_pubkey=False):
+ parser.add_argument("--transport", metavar="TRANSPORT",
+ help="register using the given transport, default %(default)s.",
+ default=DEFAULT_TRANSPORT)
+ parser.add_argument("--facilitator-pubkey", metavar="FILENAME",
+ help=(_OPTION_IGNORED if ignore_pubkey else "encrypt registrations to "
+ "the given PEM-formatted public key file (default built-in)."))
+
+ old_parse = parser.parse_args
+ def parse_args(namespace):
+ options.transport = namespace.transport
+ options.facilitator_pubkey = namespace.facilitator_pubkey
+ return namespace
+ parser.parse_args = lambda *a, **kw: parse_args(old_parse(*a, **kw))
+
+def add_registration_args(parser, **kwargs):
+ add_module_opts(parser, **kwargs)
+ parser.add_argument("remote_addr", metavar="ADDR:PORT",
+ help="external addr+port to register, default %s" %
+ format_addr(DEFAULT_REMOTE), default="", nargs="?",
+ type=lambda x: parse_addr_spec(x, *DEFAULT_REMOTE))
+
+
+def build_reg(addr, transport):
+ return urllib.urlencode((
+ ("client", format_addr(addr)),
+ ("client-transport", transport),
+ ))
+
+def build_reg_b64enc(addr, transport, urlsafe=False):
+ pubkey = get_pubkey(DEFAULT_FACILITATOR_PUBKEY_PEM, options.facilitator_pubkey)
+ return pubkey_b64enc(build_reg(addr, transport), pubkey, urlsafe=urlsafe)
+
class Transport(namedtuple("Transport", "inner outer")):
@classmethod
diff --git a/flashproxy/util.py b/flashproxy/util.py
index 13cb5a4..5df15be 100644
--- a/flashproxy/util.py
+++ b/flashproxy/util.py
@@ -1,6 +1,44 @@
import re
import socket
+_old_socket_getaddrinfo = socket.getaddrinfo
+
+class options(object):
+ safe_logging = True
+ address_family = socket.AF_UNSPEC
+
+def add_module_opts(parser):
+ parser.add_argument("-4",
+ help="name lookups use only IPv4.",
+ action="store_const", const=socket.AF_INET, dest="address_family")
+ parser.add_argument("-6",
+ help="name lookups use only IPv6.",
+ action="store_const", const=socket.AF_INET6, dest="address_family")
+ parser.add_argument("--unsafe-logging",
+ help="don't scrub IP addresses and other sensitive information from "
+ "logs.", action="store_true")
+
+ old_parse = parser.parse_args
+ def parse_args(namespace):
+ options.safe_logging = not namespace.unsafe_logging
+ options.address_family = namespace.address_family or socket.AF_UNSPEC
+ if options.address_family != socket.AF_UNSPEC:
+ def getaddrinfo_replacement(host, port, family, *args, **kwargs):
+ return _old_socket_getaddrinfo(host, port, options.address_family, *args, **kwargs)
+ socket.getaddrinfo = getaddrinfo_replacement
+ return namespace
+ parser.parse_args = lambda *a, **kw: parse_args(old_parse(*a, **kw))
+
+def safe_str(s):
+ """Return "[scrubbed]" if options.safe_logging is true, and s otherwise."""
+ if options.safe_logging:
+ return "[scrubbed]"
+ else:
+ return s
+
+def safe_format_addr(addr):
+ return safe_str(format_addr(addr))
+
def parse_addr_spec(spec, defhost = None, defport = None):
"""Parse a host:port specification and return a 2-tuple ("host", port) as
understood by the Python socket functions.
More information about the tor-commits
mailing list