Skip to content

Commit

Permalink
#1791 new ldap3 authentication module using the "ldap3" python module
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@18843 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Mar 25, 2018
1 parent 27896db commit 51368a9
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 4 deletions.
6 changes: 5 additions & 1 deletion src/man/xpra.1
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,11 @@ validates a GSS ticket obtained by the client.
.IP \fBu2f\fP
requests a U2F token from the client.
.IP \fBldap\fP
validates the username and password against an LDAP server.
validates the username and password against an LDAP server,
using the \fIpython\-ldap\fP library.
.IP \fBldap\fP
validates the username and password against an LDAP server,
using the python \fIldap3\fP library.
.RE
.PP
.TP
Expand Down
1 change: 1 addition & 0 deletions src/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,7 @@ def add_service_exe(script, icon, base_name):
add_console_exe("xpra/server/auth/sqlite_auth.py", "sqlite.ico", "SQLite_auth_tool")
add_console_exe("xpra/server/auth/win32_auth.py", "authentication.ico", "System-Auth-Test")
add_console_exe("xpra/server/auth/ldap_auth.py", "authentication.ico", "LDAP-Auth-Test")
add_console_exe("xpra/server/auth/ldap3_auth.py", "authentication.ico", "LDAP3-Auth-Test")
add_console_exe("win32/service/proxy.py", "xpra_txt.ico", "Xpra-Proxy")
add_console_exe("xpra/platform/win32/lsa_logon_lib.py", "xpra_txt.ico", "System-Logon-Test")
if client_ENABLED:
Expand Down
2 changes: 1 addition & 1 deletion src/win32/MINGW_SETUP.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ $PACMAN --noconfirm -S ${XPKG}python2-numpy ${XPKG}python2-pillow ${XPKG}cython2
$PACMAN --noconfirm -S ${XPKG}python3-numpy ${XPKG}python3-pillow ${XPKG}cython
#using easy-install for python libraries which are not packaged by mingw:
easy_install-2.7 -U -Z enum34 enum-compat
for x in rencode xxhash zeroconf lz4 websocket-client netifaces comtypes PyOpenGL PyOpenGL_accelerate websockify cffi pycparser cryptography nvidia-ml-py appdirs setproctitle pyu2f python-ldap; do
for x in rencode xxhash zeroconf lz4 websocket-client netifaces comtypes PyOpenGL PyOpenGL_accelerate websockify cffi pycparser cryptography nvidia-ml-py appdirs setproctitle pyu2f python-ldap ldap3; do
easy_install-2.7 -U -Z $x
easy_install-3.6 -U -Z $x
done
Expand Down
2 changes: 1 addition & 1 deletion src/win32/UPDATE_PYTHON_LIBS.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#using easy-install for python libraries which are not packaged by mingw:
# currently disabled, build from patched source only: websockify
# currently disabled, do not update past 1.8.x: cryptography
for x in rencode xxhash enum34 enum-compat zeroconf lz4 websocket-client comtypes PyOpenGL PyOpenGL_accelerate cffi cryptography pycparser nvidia-ml-py appdirs setproctitle netifaces pyu2f python-ldap;
for x in rencode xxhash enum34 enum-compat zeroconf lz4 websocket-client comtypes PyOpenGL PyOpenGL_accelerate cffi cryptography pycparser nvidia-ml-py appdirs setproctitle netifaces pyu2f python-ldap ldap3;
do
easy_install-2.7 -U -Z $x
easy_install-3.6 -U -Z $x
Expand Down
131 changes: 131 additions & 0 deletions src/xpra/server/auth/ldap3_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#!/usr/bin/env python
# This file is part of Xpra.
# Copyright (C) 2018 Antoine Martin <antoine@devloop.org.uk>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os
import sys
from ldap3 import Server, Connection, Tls, ALL, SIMPLE, SASL, NTLM #@UnresolvedImport

from xpra.util import csv, obsc
from xpra.server.auth.sys_auth_base import SysAuthenticatorBase, init, log
from xpra.log import enable_debug_for
assert init and log #tests will disable logging from here

def init(opts):
pass

MECHANISM = {
"SIMPLE" : SIMPLE,
"SASL" : SASL,
"NTLM" : NTLM,
}


LDAP_CACERTFILE = os.environ.get("XPRA_LDAP_CACERTFILE")


class Authenticator(SysAuthenticatorBase):

def __init__(self, username, **kwargs):
self.tls = bool(int(kwargs.pop("tls", "0")))
self.host = kwargs.pop("host", "localhost")
self.cacert = kwargs.pop("cacert", LDAP_CACERTFILE)
self.tls_version = None
self.tls_validate = None
if self.tls:
import ssl
tls_version = kwargs.pop("ssl-version", "TLSv1")
tls_validate = kwargs.pop("ssl-validate", "REQUIRED")
self.tls_version = getattr(ssl, "PROTOCOL_%s" % tls_version)
self.tls_validate = getattr(ssl, "CERT_%s" % tls_validate)
default_port = 636
else:
default_port = 389
self.port = int(kwargs.pop("port", default_port))
self.authentication = kwargs.pop("authentication", "NTLM").upper()
assert self.authentication in MECHANISM, "invalid authentication mechanism '%s', must be one of: %s" % (self.authentication, csv(MECHANISM.keys()))
username = kwargs.pop("username", username)
SysAuthenticatorBase.__init__(self, username, **kwargs)
log("ldap auth: host=%s, port=%i, tls=%s",
self.host, self.port, self.tls)

def get_uid(self):
return self.uid

def get_gid(self):
return self.gid

def __repr__(self):
return "ldap"

def get_challenge(self, digests):
if "xor" not in digests:
log.error("Error: ldap authentication requires the 'xor' digest")
return None
return SysAuthenticatorBase.get_challenge(self, ["xor"])

def check(self, password):
log("check(%s)", obsc(password))
try:
authentication = MECHANISM[self.authentication]
tls = None
if self.tls:
tls = Tls(validate=self.tls_validate, version=self.tls_version, ca_certs_file=self.cacert)
log("TLS=%s", tls)
server = Server(self.host, port=self.port, tls=tls, use_ssl=self.tls, get_info=ALL)
log("check Server(%s)=%s", (self.host, self.port, self.tls), server)
conn = Connection(server, user=self.username, password=password, authentication=authentication, receive_timeout=10)
if self.tls:
conn.start_tls()
r = conn.bind()
log("check %s.bind()=%s", conn, r)
if not r:
return False
log("check Connection(%s, %s, %s)=%s", server, self.username, self.authentication, conn)
log("check who_am_i()=%s", conn.extend.standard.who_am_i())
return True
except Exception as e:
log("check(..)", exc_info=True)
log.error("Error: ldap3 authentication failed:")
log.error(" %s", e)
return False


def main(argv):
from xpra.util import xor
from xpra.net.crypto import get_salt, get_digests, gendigest
from xpra.platform import program_context
with program_context("LDAP3-Password-Auth", "LDAP3-Password-Authentication"):
for x in list(argv):
if x=="-v" or x=="--verbose":
enable_debug_for("auth")
argv.remove(x)
if len(argv) not in (3,4,5,6):
sys.stderr.write("%s invalid arguments\n" % argv[0])
sys.stderr.write("usage: %s username password [host] [port] [tls]\n" % argv[0])
return 1
username = argv[1]
password = argv[2]
kwargs = {}
if len(argv)>=4:
kwargs["host"] = argv[3]
if len(argv)>=5:
kwargs["port"] = argv[4]
if len(argv)>=6:
kwargs["tls"] = argv[5]
a = Authenticator(username, **kwargs)
server_salt, digest = a.get_challenge(["xor"])
salt_digest = a.choose_salt_digest(get_digests())
assert digest=="xor"
client_salt = get_salt(len(server_salt))
combined_salt = gendigest(salt_digest, client_salt, server_salt)
response = xor(password, combined_salt)
r = a.authenticate(response, client_salt)
print("success: %s" % r)
return int(not r)


if __name__ == "__main__":
sys.exit(main(sys.argv))
2 changes: 1 addition & 1 deletion src/xpra/server/server_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1052,7 +1052,7 @@ def guess_header_protocol(self, v):
except:
c = int(v[0])
import binascii
netlog("guess_header_protocol(%s)", binascii.hexlify(strtobytes(v)))
netlog.info("guess_header_protocol(%s)", binascii.hexlify(strtobytes(v)))
if c==0x16:
return "ssl", "SSL packet?"
s = bytestostr(v)
Expand Down

0 comments on commit 51368a9

Please sign in to comment.