summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2015-02-23 14:45:48 -0400
committerKali Kaneko <kali@leap.se>2015-02-23 14:45:48 -0400
commit1f4e0c32f49337e216eaaac83a8eeca9674370a4 (patch)
treec20f0ff4a6e177667f3a2233f2ef6eb7e2ee9233
parent4bad7062364c72215da9fd2256451c7a3d0a356f (diff)
parent04be045242b0fd0c669610f54db7a9fdeb3b77f3 (diff)
Merge remote-tracking branch 'leapcode/pr/826' into release/0.8.x
-rw-r--r--changes/non-curve-zmq1
-rw-r--r--src/leap/bitmask/backend/backend.py43
-rw-r--r--src/leap/bitmask/backend/backend_proxy.py25
-rw-r--r--src/leap/bitmask/backend/signaler.py25
-rw-r--r--src/leap/bitmask/backend/signaler_qt.py39
-rw-r--r--src/leap/bitmask/backend/utils.py54
-rw-r--r--src/leap/bitmask/backend_app.py4
-rw-r--r--src/leap/bitmask/config/flags.py3
8 files changed, 142 insertions, 52 deletions
diff --git a/changes/non-curve-zmq b/changes/non-curve-zmq
new file mode 100644
index 00000000..58737723
--- /dev/null
+++ b/changes/non-curve-zmq
@@ -0,0 +1 @@
+- Gracefully fall back to ZMQ ipc sockets with restricted access if CurveZMQ is not available (Feature #6646)
diff --git a/src/leap/bitmask/backend/backend.py b/src/leap/bitmask/backend/backend.py
index 75eff8a9..cff731ba 100644
--- a/src/leap/bitmask/backend/backend.py
+++ b/src/leap/bitmask/backend/backend.py
@@ -20,6 +20,7 @@
# TODO use txzmq bindings instead.
import json
+import os
import threading
import time
@@ -28,10 +29,14 @@ import psutil
from twisted.internet import defer, reactor, threads
import zmq
-from zmq.auth.thread import ThreadAuthenticator
+try:
+ from zmq.auth.thread import ThreadAuthenticator
+except ImportError:
+ pass
from leap.bitmask.backend.api import API, PING_REQUEST
from leap.bitmask.backend.utils import get_backend_certificates
+from leap.bitmask.config import flags
from leap.bitmask.backend.signaler import Signaler
import logging
@@ -43,12 +48,15 @@ class Backend(object):
Backend server.
Receives signals from backend_proxy and emit signals if needed.
"""
- # XXX this should not be hardcoded. Make it configurable.
- PORT = '5556'
-
# XXX we might want to make this configurable per-platform,
# and use the most performant socket type on each one.
- BIND_ADDR = "tcp://127.0.0.1:%s" % PORT
+ if flags.ZMQ_HAS_CURVE:
+ # XXX this should not be hardcoded. Make it configurable.
+ PORT = '5556'
+ BIND_ADDR = "tcp://127.0.0.1:%s" % PORT
+ else:
+ SOCKET_FILE = "/tmp/bitmask.socket.0"
+ BIND_ADDR = "ipc://%s" % SOCKET_FILE
PING_INTERVAL = 2 # secs
@@ -73,20 +81,23 @@ class Backend(object):
context = zmq.Context()
socket = context.socket(zmq.REP)
- # Start an authenticator for this context.
- auth = ThreadAuthenticator(context)
- auth.start()
- # XXX do not hardcode this here.
- auth.allow('127.0.0.1')
+ if flags.ZMQ_HAS_CURVE:
+ # Start an authenticator for this context.
+ auth = ThreadAuthenticator(context)
+ auth.start()
+ # XXX do not hardcode this here.
+ auth.allow('127.0.0.1')
- # Tell authenticator to use the certificate in a directory
- auth.configure_curve(domain='*', location=zmq.auth.CURVE_ALLOW_ANY)
- public, secret = get_backend_certificates()
- socket.curve_publickey = public
- socket.curve_secretkey = secret
- socket.curve_server = True # must come before bind
+ # Tell authenticator to use the certificate in a directory
+ auth.configure_curve(domain='*', location=zmq.auth.CURVE_ALLOW_ANY)
+ public, secret = get_backend_certificates()
+ socket.curve_publickey = public
+ socket.curve_secretkey = secret
+ socket.curve_server = True # must come before bind
socket.bind(self.BIND_ADDR)
+ if not flags.ZMQ_HAS_CURVE:
+ os.chmod(self.SOCKET_FILE, 0600)
self._zmq_socket = socket
diff --git a/src/leap/bitmask/backend/backend_proxy.py b/src/leap/bitmask/backend/backend_proxy.py
index 3e79289f..b2f79a70 100644
--- a/src/leap/bitmask/backend/backend_proxy.py
+++ b/src/leap/bitmask/backend/backend_proxy.py
@@ -30,6 +30,7 @@ import zmq
from leap.bitmask.backend.api import API, STOP_REQUEST, PING_REQUEST
from leap.bitmask.backend.utils import generate_zmq_certificates_if_needed
from leap.bitmask.backend.utils import get_backend_certificates
+from leap.bitmask.config import flags
import logging
logger = logging.getLogger(__name__)
@@ -41,8 +42,11 @@ class BackendProxy(object):
to the backend.
"""
- PORT = '5556'
- SERVER = "tcp://localhost:%s" % PORT
+ if flags.ZMQ_HAS_CURVE:
+ PORT = '5556'
+ SERVER = "tcp://localhost:%s" % PORT
+ else:
+ SERVER = "ipc:///tmp/bitmask.socket.0"
POLL_TIMEOUT = 4000 # ms
POLL_TRIES = 3
@@ -59,15 +63,16 @@ class BackendProxy(object):
logger.debug("Connecting to server...")
socket = context.socket(zmq.REQ)
- # public, secret = zmq.curve_keypair()
- client_keys = zmq.curve_keypair()
- socket.curve_publickey = client_keys[0]
- socket.curve_secretkey = client_keys[1]
+ if flags.ZMQ_HAS_CURVE:
+ # public, secret = zmq.curve_keypair()
+ client_keys = zmq.curve_keypair()
+ socket.curve_publickey = client_keys[0]
+ socket.curve_secretkey = client_keys[1]
- # The client must know the server's public key to make a CURVE
- # connection.
- public, _ = get_backend_certificates()
- socket.curve_serverkey = public
+ # The client must know the server's public key to make a CURVE
+ # connection.
+ public, _ = get_backend_certificates()
+ socket.curve_serverkey = public
socket.setsockopt(zmq.RCVTIMEO, 1000)
socket.setsockopt(zmq.LINGER, 0) # Terminate early
diff --git a/src/leap/bitmask/backend/signaler.py b/src/leap/bitmask/backend/signaler.py
index 43cba994..aec2f606 100644
--- a/src/leap/bitmask/backend/signaler.py
+++ b/src/leap/bitmask/backend/signaler.py
@@ -26,6 +26,7 @@ import zmq
from leap.bitmask.backend.api import SIGNALS
from leap.bitmask.backend.utils import get_frontend_certificates
+from leap.bitmask.config import flags
import logging
logger = logging.getLogger(__name__)
@@ -36,8 +37,11 @@ class Signaler(object):
Signaler client.
Receives signals from the backend and sends to the signaling server.
"""
- PORT = "5667"
- SERVER = "tcp://localhost:%s" % PORT
+ if flags.ZMQ_HAS_CURVE:
+ PORT = "5667"
+ SERVER = "tcp://localhost:%s" % PORT
+ else:
+ SERVER = "ipc:///tmp/bitmask.socket.1"
POLL_TIMEOUT = 2000 # ms
POLL_TRIES = 500
@@ -49,15 +53,16 @@ class Signaler(object):
logger.debug("Connecting to signaling server...")
socket = context.socket(zmq.REQ)
- # public, secret = zmq.curve_keypair()
- client_keys = zmq.curve_keypair()
- socket.curve_publickey = client_keys[0]
- socket.curve_secretkey = client_keys[1]
+ if flags.ZMQ_HAS_CURVE:
+ # public, secret = zmq.curve_keypair()
+ client_keys = zmq.curve_keypair()
+ socket.curve_publickey = client_keys[0]
+ socket.curve_secretkey = client_keys[1]
- # The client must know the server's public key to make a CURVE
- # connection.
- public, _ = get_frontend_certificates()
- socket.curve_serverkey = public
+ # The client must know the server's public key to make a CURVE
+ # connection.
+ public, _ = get_frontend_certificates()
+ socket.curve_serverkey = public
socket.setsockopt(zmq.RCVTIMEO, 1000)
socket.setsockopt(zmq.LINGER, 0) # Terminate early
diff --git a/src/leap/bitmask/backend/signaler_qt.py b/src/leap/bitmask/backend/signaler_qt.py
index 433f18ed..b7f48d21 100644
--- a/src/leap/bitmask/backend/signaler_qt.py
+++ b/src/leap/bitmask/backend/signaler_qt.py
@@ -18,16 +18,21 @@
Signaling server.
Receives signals from the signaling client and emit Qt signals for the GUI.
"""
+import os
import threading
import time
from PySide import QtCore
import zmq
-from zmq.auth.thread import ThreadAuthenticator
+try:
+ from zmq.auth.thread import ThreadAuthenticator
+except ImportError:
+ pass
from leap.bitmask.backend.api import SIGNALS
from leap.bitmask.backend.utils import get_frontend_certificates
+from leap.bitmask.config import flags
import logging
logger = logging.getLogger(__name__)
@@ -38,8 +43,12 @@ class SignalerQt(QtCore.QObject):
Signaling server.
Receives signals from the signaling client and emit Qt signals for the GUI.
"""
- PORT = "5667"
- BIND_ADDR = "tcp://127.0.0.1:%s" % PORT
+ if flags.ZMQ_HAS_CURVE:
+ PORT = "5667"
+ BIND_ADDR = "tcp://127.0.0.1:%s" % PORT
+ else:
+ SOCKET_FILE = "/tmp/bitmask.socket.1"
+ BIND_ADDR = "ipc://%s" % SOCKET_FILE
def __init__(self):
QtCore.QObject.__init__(self)
@@ -67,20 +76,24 @@ class SignalerQt(QtCore.QObject):
context = zmq.Context()
socket = context.socket(zmq.REP)
- # Start an authenticator for this context.
- auth = ThreadAuthenticator(context)
- auth.start()
- auth.allow('127.0.0.1')
+ if flags.ZMQ_HAS_CURVE:
+ # Start an authenticator for this context.
+ auth = ThreadAuthenticator(context)
+ auth.start()
+ auth.allow('127.0.0.1')
- # Tell authenticator to use the certificate in a directory
- auth.configure_curve(domain='*', location=zmq.auth.CURVE_ALLOW_ANY)
- public, secret = get_frontend_certificates()
- socket.curve_publickey = public
- socket.curve_secretkey = secret
- socket.curve_server = True # must come before bind
+ # Tell authenticator to use the certificate in a directory
+ auth.configure_curve(domain='*', location=zmq.auth.CURVE_ALLOW_ANY)
+ public, secret = get_frontend_certificates()
+ socket.curve_publickey = public
+ socket.curve_secretkey = secret
+ socket.curve_server = True # must come before bind
socket.bind(self.BIND_ADDR)
+ if not flags.ZMQ_HAS_CURVE:
+ os.chmod(self.SOCKET_FILE, 0600)
+
while self._do_work.is_set():
# Wait for next request from client
try:
diff --git a/src/leap/bitmask/backend/utils.py b/src/leap/bitmask/backend/utils.py
index 18e70743..b2674330 100644
--- a/src/leap/bitmask/backend/utils.py
+++ b/src/leap/bitmask/backend/utils.py
@@ -22,20 +22,63 @@ import os
import shutil
import stat
-import zmq.auth
+import zmq
+try:
+ import zmq.auth
+except ImportError:
+ pass
+
+from leap.bitmask.config import flags
from leap.bitmask.util import get_path_prefix
from leap.common.files import mkdir_p
+from leap.common.check import leap_assert
logger = logging.getLogger(__name__)
KEYS_DIR = os.path.join(get_path_prefix(), 'leap', 'zmq_certificates')
+def _zmq_has_curve():
+ """
+ Return whether the current ZMQ has support for auth and CurveZMQ security.
+
+ :rtype: bool
+
+ Version notes:
+ `zmq.curve_keypair()` is new in version 14.0, new in version libzmq-4.0.
+ Requires libzmq (>= 4.0) to have been linked with libsodium.
+ `zmq.auth` module is new in version 14.1
+ `zmq.has()` is new in version 14.1, new in version libzmq-4.1.
+ """
+ zmq_version = zmq.zmq_version_info()
+ pyzmq_version = zmq.pyzmq_version_info()
+
+ if pyzmq_version >= (14, 1, 0) and zmq_version >= (4, 1):
+ return zmq.has('curve')
+
+ if pyzmq_version < (14, 1, 0):
+ return False
+
+ if zmq_version < (4, 0):
+ # security is new in libzmq 4.0
+ return False
+
+ try:
+ zmq.curve_keypair()
+ except zmq.error.ZMQError:
+ # security requires libzmq to be linked against libsodium
+ return False
+
+ return True
+
+
def generate_zmq_certificates():
"""
Generate client and server CURVE certificate files.
"""
+ leap_assert(flags.ZMQ_HAS_CURVE, "CurveZMQ not supported!")
+
# Create directory for certificates, remove old content if necessary
if os.path.exists(KEYS_DIR):
shutil.rmtree(KEYS_DIR)
@@ -53,6 +96,8 @@ def get_frontend_certificates():
"""
Return the frontend's public and secret certificates.
"""
+ leap_assert(flags.ZMQ_HAS_CURVE, "CurveZMQ not supported!")
+
frontend_secret_file = os.path.join(KEYS_DIR, "frontend.key_secret")
public, secret = zmq.auth.load_certificate(frontend_secret_file)
return public, secret
@@ -62,6 +107,8 @@ def get_backend_certificates(base_dir='.'):
"""
Return the backend's public and secret certificates.
"""
+ leap_assert(flags.ZMQ_HAS_CURVE, "CurveZMQ not supported!")
+
backend_secret_file = os.path.join(KEYS_DIR, "backend.key_secret")
public, secret = zmq.auth.load_certificate(backend_secret_file)
return public, secret
@@ -84,5 +131,8 @@ def generate_zmq_certificates_if_needed():
Generate the needed ZMQ certificates for backend/frontend communication if
needed.
"""
- if not _certificates_exist():
+ if flags.ZMQ_HAS_CURVE and not _certificates_exist():
generate_zmq_certificates()
+
+
+flags.ZMQ_HAS_CURVE = _zmq_has_curve()
diff --git a/src/leap/bitmask/backend_app.py b/src/leap/bitmask/backend_app.py
index 286b04f7..3e88a95a 100644
--- a/src/leap/bitmask/backend_app.py
+++ b/src/leap/bitmask/backend_app.py
@@ -23,6 +23,7 @@ import signal
from leap.bitmask.backend.leapbackend import LeapBackend
from leap.bitmask.backend.utils import generate_zmq_certificates
+from leap.bitmask.config import flags
from leap.bitmask.logs.utils import create_logger
from leap.bitmask.util import dict_to_flags
@@ -57,7 +58,8 @@ def run_backend(bypass_checks=False, flags_dict=None, frontend_pid=None):
"""
# The backend is the one who always creates the certificates. Either if it
# is run separately or in a process in the same app as the frontend.
- generate_zmq_certificates()
+ if flags.ZMQ_HAS_CURVE:
+ generate_zmq_certificates()
# ignore SIGINT since app.py takes care of signaling SIGTERM to us.
signal.signal(signal.SIGINT, signal.SIG_IGN)
diff --git a/src/leap/bitmask/config/flags.py b/src/leap/bitmask/config/flags.py
index 6b70659d..cdde1971 100644
--- a/src/leap/bitmask/config/flags.py
+++ b/src/leap/bitmask/config/flags.py
@@ -55,3 +55,6 @@ OPENVPN_VERBOSITY = 1
# Skip the checks in the wizard, use for testing purposes only!
SKIP_WIZARD_CHECKS = False
+
+# This flag tells us whether the current pyzmq supports using CurveZMQ or not.
+ZMQ_HAS_CURVE = None