Hi, This patch implements secure passwords authentication (I believe - can someone please check!)
If password is enabled (via the command line switch), the server sends a challenge packet (with the salt in it) when it receives the "hello" command. The client must re-send the hello command with the password hashed with the salt. The only part that I am not too sure about is the sending of the challenge packet: at this point, self._protocol is not set, so I used: proto._sock.send(data) Which may not be the right thing to do as it does not deal with errors or short writes... But it felt wrong to add a whole lot of code just to send ~50 bytes! Comments welcome. Antoine
Index: trunk/dev/xpra/client.py
===================================================================
--- trunk/dev/xpra/client.py (revision 74)
+++ trunk/dev/xpra/client.py (working copy)
@@ -9,6 +9,7 @@
import os
import os.path
import sys
+import hashlib
from wimpiggy.util import (one_arg_signal,
gtk_main_quit_really,
@@ -268,18 +269,17 @@
gobject.type_register(ClientWindow)
class XpraClient(gobject.GObject):
- def __init__(self, sock, compression_level, title_suffix):
+ def __init__(self, sock, compression_level, title_suffix, password):
gobject.GObject.__init__(self)
self._window_to_id = {}
self._id_to_window = {}
self.title_suffix = title_suffix
+ self.password = password
+ self.compression_level = compression_level
self._protocol = Protocol(sock, self.process_packet)
ClientSource(self._protocol)
- capabilities_request = dict(default_capabilities)
- if compression_level:
- capabilities_request["deflate"] = compression_level
- self.send(["hello", capabilities_request])
+ self.send_hello()
self._keymap = gtk.gdk.keymap_get_default()
self._keymap.connect("keys-changed", self._keys_changed)
@@ -320,6 +320,25 @@
def send_mouse_position(self, packet):
self._protocol.source.queue_mouse_position_packet(packet)
+ def send_hello(self, hash=None):
+ capabilities_request = dict(default_capabilities)
+ if hash:
+ capabilities_request["challenge_response"] = hash
+ if self.compression_level:
+ capabilities_request["deflate"] = self.compression_level
+ self.send(["hello", capabilities_request])
+
+ def _process_challenge(self, packet):
+ if not self.password:
+ log.error("password is required by the server")
+ gtk.main_quit()
+ return
+ (_, salt) = packet
+ hash = hashlib.sha1()
+ hash.update(salt)
+ hash.update(self.password)
+ self.send_hello(hash.hexdigest())
+
def _process_hello(self, packet):
(_, capabilities) = packet
if "deflate" in capabilities:
@@ -372,6 +391,7 @@
gtk_main_quit_really()
_packet_handlers = {
+ "challenge": _process_challenge,
"hello": _process_hello,
"new-window": _process_new_window,
"new-override-redirect": _process_new_override_redirect,
Index: trunk/dev/xpra/scripts/server.py
===================================================================
--- trunk/dev/xpra/scripts/server.py (revision 74)
+++ trunk/dev/xpra/scripts/server.py (working copy)
@@ -284,7 +284,7 @@
# This import is delayed because the module depends on gtk:
import xpra.server
- app = xpra.server.XpraServer(upgrading, sockets)
+ app = xpra.server.XpraServer(upgrading, sockets, opts.password)
def cleanup_socket(self):
print "removing socket"
try:
Index: trunk/dev/xpra/server.py
===================================================================
--- trunk/dev/xpra/server.py (revision 74)
+++ trunk/dev/xpra/server.py (working copy)
@@ -15,6 +15,8 @@
import os
import os.path
import subprocess
+import hashlib
+import uuid
from wimpiggy.wm import Wm
from wimpiggy.util import (LameStruct,
@@ -207,7 +209,7 @@
"wimpiggy-child-map-event": one_arg_signal,
}
- def __init__(self, clobber, sockets):
+ def __init__(self, clobber, sockets, password):
gobject.GObject.__init__(self)
# Do this before creating the Wm object, to avoid clobbering its
@@ -289,6 +291,9 @@
self._has_focus = 0
self._upgrading = False
+ self.password = password
+ self.salt = None
+
### All right, we're ready to accept customers:
self._protocol = None
self._potential_protocols = []
@@ -483,7 +488,7 @@
def _calculate_capabilities(self, client_capabilities):
capabilities = {}
- for cap in ("deflate", "__prerelease_version"):
+ for cap in ("deflate", "__prerelease_version", "challenge_response"):
if cap in client_capabilities:
capabilities[cap] = client_capabilities[cap]
return capabilities
@@ -497,6 +502,30 @@
+ "of exactly the same version (v%s)", xpra.__version__)
proto.close()
return
+ if self.password:
+ log.debug("password auth required")
+ client_hash = capabilities.get("challenge_response")
+ if not client_hash or not self.salt:
+ self.salt = "%s" % uuid.uuid4()
+ capabilities["challenge"] = self.salt
+ log.info("Password required, sending challenge: %s" % str(capabilities))
+ packet = ("challenge", self.salt)
+ from xpra.bencode import bencode
+ data = bencode(packet)
+ proto._sock.send(data)
+ return
+
+ hash = hashlib.sha1()
+ hash.update(self.salt)
+ hash.update(self.password)
+ if client_hash != hash.hexdigest():
+ log.error("Password supplied does not match! dropping the connection.")
+ proto.close()
+ return
+ else:
+ log.info("Password matches!")
+ del capabilities["challenge_response"]
+ self.salt = None #prevent replay attacks
# Okay, things are okay, so let's boot out any existing connection and
# set this as our new one:
if self._protocol is not None:
Index: trunk/dev/xpra/scripts/main.py
===================================================================
--- trunk/dev/xpra/scripts/main.py (revision 74)
+++ trunk/dev/xpra/scripts/main.py (working copy)
@@ -53,6 +53,9 @@
dest="bind_tcp", default=None,
metavar="[HOST]:PORT",
help="Listen for connections over TCP (insecure)")
+ parser.add_option("--password", action="store",
+ dest="password", default=None,
+ help="Password required to connect (useful to secure TCP mode)")
parser.add_option("--title-suffix", action="store",
dest="title_suffix", default=" (via xpra)",
help="Text which is appended to the window's title")
@@ -164,7 +167,7 @@
sock, local = client_sock(parser, opts, pick_display(parser, extra_args))
if opts.compression_level < 0 or opts.compression_level > 9:
parser.error("Compression level must be between 0 and 9 inclusive.")
- app = XpraClient(sock, opts.compression_level, opts.title_suffix)
+ app = XpraClient(sock, opts.compression_level, opts.title_suffix, opts.password)
sys.stdout.write("Attached\n")
app.run()
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Parti-discuss mailing list [email protected] http://lists.partiwm.org/cgi-bin/mailman/listinfo/parti-discuss
