This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository x2gobroker. commit 5cd512f08f9f0fe525f59f09728bfaa96af7a8c3 Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de> Date: Fri Sep 14 12:53:37 2018 +0200 Move AuthService and AuthClient classes to x2gobroker/authservice.py, so that we have them in the API documentation. --- sbin/x2gobroker-authservice | 56 +----------------------- x2gobroker/authservice.py | 103 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 55 deletions(-) diff --git a/sbin/x2gobroker-authservice b/sbin/x2gobroker-authservice index baa75c8..c80a86f 100755 --- a/sbin/x2gobroker-authservice +++ b/sbin/x2gobroker-authservice @@ -25,10 +25,8 @@ import setproctitle import argparse import logging import asyncore -import socket import getpass import logging.config -import pam import atexit import configparser @@ -55,59 +53,7 @@ setproctitle.setproctitle("%s %s" % (PROG_NAME, " ".join(PROG_OPTIONS))) from x2gobroker import __VERSION__ from x2gobroker import __AUTHOR__ - - -class AuthClient(asyncore.dispatcher_with_send): - - def __init__(self, sock, logger=None): - self.logger = logger - asyncore.dispatcher_with_send.__init__(self, sock) - self._buf = '' - - def handle_read(self): - data = self._buf + self.recv(1024).decode() - if not data: - self.close() - return - reqs, data = data.rsplit('\n', 1) - self._buf = data - for req in reqs.split('\n'): - try: - user, passwd, service = req.split('\r') - except: - self.send('bad\n') - self.logger.warning('bad authentication data received') - else: - opam = pam - if hasattr(pam, "pam"): - opam = pam.pam() - if opam.authenticate(user, passwd, service): - self.send('ok\n'.encode()) - self.logger.info('successful authentication for \'{user}\' with password \'<hidden>\' against PAM service \'{service}\''.format(user=user, service=service)) - else: - self.send('fail\n'.encode()) - self.logger.info('authentication failure for \'{user}\' with password \'<hidden>\' against PAM service \'{service}\''.format(user=user, service=service)) - - def handle_close(self): - self.close() - - -class AuthService(asyncore.dispatcher_with_send): - - def __init__(self, socketfile, owner='root', group_owner='root', permissions='0o660', logger=None): - self.logger = logger - asyncore.dispatcher_with_send.__init__(self) - self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM) - self.set_reuse_addr() - self.bind(socketfile) - os.chown(socketfile, getpwnam(owner).pw_uid, getgrnam(group_owner).gr_gid) - os.chmod(socketfile, int(permissions, 8)) - self.listen(1) - - def handle_accept(self): - conn, _ = self.accept() - AuthClient(conn, logger=self.logger) - +from x2gobroker.authservice import AuthService def loop(): asyncore.loop() diff --git a/x2gobroker/authservice.py b/x2gobroker/authservice.py index d8d6755..2bac9bc 100644 --- a/x2gobroker/authservice.py +++ b/x2gobroker/authservice.py @@ -18,8 +18,14 @@ # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +import asyncore +import os +import pam import socket +from pwd import getpwnam +from grp import getgrnam + # X2Go Session Broker modules import x2gobroker.defaults from x2gobroker.loggers import logger_broker @@ -69,3 +75,100 @@ def authenticate(username, password, service="x2gobroker"): return True logger_broker.info('authservice.authenticate(): authentication against service »{service}« failed for user »{username}«'.format(username=username, service=service)) return False + +class AuthClient(asyncore.dispatcher_with_send): + """\ + Handle incoming PAM credential verification request and send a + response back through the socket. + + :param sock: open socket connection + :type sock: ``<obj>`` + :param logger: logger instance to report log messages to + :type logger: ``obj`` + + """ + def __init__(self, sock, logger=None): + self.logger = logger + asyncore.dispatcher_with_send.__init__(self, sock) + self._buf = '' + + def handle_read(self): + """\ + Handle the incoming request after :func:`AuthService.accept()` + and respond accordingly. + + The requests are expected line by line, the fields are split by "\\r":: + + <user>\\r<password>\\r<pam-service>\\n + + The reponse is sent back over the open socket connection. + Possibly answers are either:: + + ok\\n + + or... + + fail\\n + + """ + data = self._buf + self.recv(1024).decode() + if not data: + self.close() + return + reqs, data = data.rsplit('\n', 1) + self._buf = data + for req in reqs.split('\n'): + try: + user, passwd, service = req.split('\r') + except: + self.send('bad\n') + self.logger.warning('bad authentication data received') + else: + opam = pam + if hasattr(pam, "pam"): + opam = pam.pam() + if opam.authenticate(user, passwd, service): + self.send('ok\n'.encode()) + self.logger.info('successful authentication for \'{user}\' with password \'<hidden>\' against PAM service \'{service}\''.format(user=user, service=service)) + else: + self.send('fail\n'.encode()) + self.logger.info('authentication failure for \'{user}\' with password \'<hidden>\' against PAM service \'{service}\''.format(user=user, service=service)) + + def handle_close(self): + self.close() + + +class AuthService(asyncore.dispatcher_with_send): + """\ + Provide an :mod:`asyncore` based authentication socket handler where + client can send credential checking requests to. + + Access to the sockt is limited by file permissions to given owner and + group. + + :param socketfile: file name path of the to be created Unix domain + socket file. The directory in the give path must exist. + :type socketfile: ``str`` + :param owner: chown the socket file to this owner + :type owner: ``str`` + :param group: chgrp the socket file to this group + :type group: ``str`` + :param permissions: octal representation of the file permissions (handed over as string) + :type permissions: ``str`` + :param logger: logger instance to report log messages to + :type logger: ``<obj>`` + + """ + def __init__(self, socketfile, owner='root', group_owner='root', permissions='0o660', logger=None): + self.logger = logger + asyncore.dispatcher_with_send.__init__(self) + self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.set_reuse_addr() + self.bind(socketfile) + os.chown(socketfile, getpwnam(owner).pw_uid, getgrnam(group_owner).gr_gid) + os.chmod(socketfile, int(permissions, 8)) + self.listen(1) + + def handle_accept(self): + conn, _ = self.accept() + AuthClient(conn, logger=self.logger) -- Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git