Hello community, here is the log from the commit of package python-slapdsock for openSUSE:Factory checked in at 2017-07-04 09:11:49 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-slapdsock (Old) and /work/SRC/openSUSE:Factory/.python-slapdsock.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-slapdsock" Tue Jul 4 09:11:49 2017 rev:3 rq:507844 version:0.5.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-slapdsock/python-slapdsock.changes 2017-03-20 17:13:41.882836516 +0100 +++ /work/SRC/openSUSE:Factory/.python-slapdsock.new/python-slapdsock.changes 2017-07-04 09:12:06.468151731 +0200 @@ -1,0 +2,6 @@ +Sun Jul 2 16:20:19 UTC 2017 - [email protected] + +- update to upstream release 0.5.3 +- added source archive signature check + +------------------------------------------------------------------- Old: ---- slapdsock-0.5.2.tar.gz New: ---- python-slapdsock.keyring slapdsock-0.5.3.tar.gz slapdsock-0.5.3.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-slapdsock.spec ++++++ --- /var/tmp/diff_new_pack.LSbWZ5/_old 2017-07-04 09:12:10.875531928 +0200 +++ /var/tmp/diff_new_pack.LSbWZ5/_new 2017-07-04 09:12:10.879531366 +0200 @@ -17,13 +17,15 @@ Name: python-slapdsock -Version: 0.5.2 +Version: 0.5.3 Release: 0 Summary: Python module for slapd-sock listeners License: Apache-2.0 Group: Development/Languages/Python Url: https://www.stroeder.com/slapdsockd.html -Source: https://files.pythonhosted.org/packages/source/s/slapdsock/slapdsock-%{version}.tar.gz +Source0: https://files.pythonhosted.org/packages/source/s/slapdsock/slapdsock-%{version}.tar.gz +Source1: https://files.pythonhosted.org/packages/source/s/slapdsock/slapdsock-%{version}.tar.gz.asc +Source2: python-slapdsock.keyring BuildRequires: python-devel BuildRequires: python-setuptools BuildRequires: python-ldap ++++++ python-slapdsock.keyring ++++++ pub rsa2048 2016-03-07 [SC] 43C8730E84A20E560722806C07DC7AE36A8BC938 uid [ unknown] Michael Ströder <[email protected]> sub rsa2048 2016-03-07 [E] -----BEGIN PGP PUBLIC KEY BLOCK----- mQENBFbdnRoBCADj0vYA4aRwKJ6AE4mf8oElLgMT/1eLNKpJ2FYBWcwj9d8dTk5/ p9b8DRxyS/qQIUUZqt9xRFZwUCm0vFeQMRDeN9xzAKoRzrJifoDOacOjG1lhZTKY vVZGgUT89Ao3QeHhQ7gPzcAKNoueoR2y3FXStOYuRrbk5PlSjVAITjsotgc7PWE9 mmVYpeu8a+byK/DBHKUyolOA1UXYvDa7MbPhMtdNm8qnwtKs1Vsyk1VkErM+5cIe +zTT6WYQcmZMRjCtWGiFTzk9W6MdlskkWRTKhKNgokTsgcy1ecaCBUZWxv/SyXgD 81+rwRi9b8Px+1reg43ayxi8sV7jrI1feybbABEBAAG0J01pY2hhZWwgU3Ryw7Zk ZXIgPG1pY2hhZWxAc3Ryb2VkZXIuY29tPokBNwQTAQgAIQUCVt2dGgIbAwULCQgH AgYVCAkKCwIEFgIDAQIeAQIXgAAKCRAH3HrjaovJOFpTCACjO773gcmJKvzjiNpU Fl/gANyaJgIq4VbMQ7VthRb1F9X6YbdJ6Z99ntyESjGFCpjofcSomr2vJDpv6ht+ lY33yo20YwsMpqe2OeId0jPybG+FtabKjgBNoAk7iqnBGUvE4t0dz0n1LQVCQR2j xyTKmcNqOYpsRZ3H+6kWwJMuVgsNZglINVZ8JgV5QuLYN5jhYz+pOuFnU11bV6nW REvzZXzebe7g7Zus6AsWjtJ0lDvgBNzLlF3/eFrVch6Bejs0SvuFseIdZQk+4YU6 Rb8xul/jDFXIfo7eTmijO3dVT5AmC1cUi8czncwpgAJnEH8vYv23RoN/aw2gSMCS 2huIuQENBFbdnRoBCAC7L1cTVBVZZuM/yxSUM5CsgGBlTD1Cr7C2ngZFsHSYXVLq 6NUB8GZA2iLK96CrwnFw4/Jjz4llOjc50iVRMQKLRyFWOJAMrpPq2ew5T+Uoo524 D//dwVbqkFVVuvM8NPiKIDyPGCjP+acM1D8hXwhOXgQ8Iz8Q3/GRSYjitn9JrkF0 ia2nhariznBKVu0LDffxF/hOCx45+QRR2/rYYlshfZMB7nEJX9P+hVfMCSzltz9Z 8CldeUbiJvnyrISReR2XBw9oh8JkIUP0BtpIaify9A7EfzOk+W9BUnWe+YwdSUsB fJxOhSv+umyW5GMqZGFu+4oYnkzbe+1LUs1JarCtABEBAAGJAR8EGAEIAAkFAlbd nRoCGwwACgkQB9x642qLyTjEUgf+JX6Atatl/QKe37yCj1OZYNPd3B0rPLJRF5mE mrADRXLZC9+uFeDSWxxln040gnR6rjBHrRcvVmlTDiZY26iuL16+V+0/aZ9uyXNQ Szk2cwDSiI/8gvr72Y+FN5fhcGXpeNHxHilYc9onzDhxyE76cwzqTKm4q2ULIH2u 9IHQ5O86Fv6nHPYhe2fy1bhQapNwi/Xl3G3i2WNH/w7m+1zWU1IddZOjmXzoxLT1 BATwXGa0Tt5RjVb2mM1Wg3Zj6kqFkF2vvKcvrwj0q0Ap5uyfN5m0uWzQMCMoaV9H Qf7f5MkS1lnwBqDgnojjVAieX5uk7olUiRuPKHMfhvXulYP8AA== =D+4K -----END PGP PUBLIC KEY BLOCK----- ++++++ slapdsock-0.5.2.tar.gz -> slapdsock-0.5.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/PKG-INFO new/slapdsock-0.5.3/PKG-INFO --- old/slapdsock-0.5.2/PKG-INFO 2017-03-20 11:45:10.000000000 +0100 +++ new/slapdsock-0.5.3/PKG-INFO 2017-07-02 18:18:43.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: slapdsock -Version: 0.5.2 +Version: 0.5.3 Summary: Module package for back-sock listeners for OpenLDAP Home-page: https://www.stroeder.com/slapdsock.html Author: Michael Stroeder Binary files old/slapdsock-0.5.2/examples/openldap/accesslog/data.mdb and new/slapdsock-0.5.3/examples/openldap/accesslog/data.mdb differ Binary files old/slapdsock-0.5.2/examples/openldap/accesslog/lock.mdb and new/slapdsock-0.5.3/examples/openldap/accesslog/lock.mdb differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/examples/openldap/realdb.conf new/slapdsock-0.5.3/examples/openldap/realdb.conf --- old/slapdsock-0.5.2/examples/openldap/realdb.conf 2017-03-20 10:43:48.000000000 +0100 +++ new/slapdsock-0.5.3/examples/openldap/realdb.conf 2017-07-01 23:36:38.000000000 +0200 @@ -4,11 +4,11 @@ database mdb -suffix "ou=realdb,dc=example,dc=org" +suffix "ou=data,dc=example,dc=org" directory realdb -rootdn "cn=root,ou=realdb,dc=example,dc=org" +rootdn "cn=root,ou=data,dc=example,dc=org" rootpw testsecret lastmod on @@ -16,11 +16,27 @@ overlay sock extensions binddn peername ssf connid -socketpath sockoverlay-listener -sockops bind unbind search compare modify modrdn add delete -sockresps result search +socketpath /tmp/modify-listener +sockops modify + +#overlay sock +#extensions binddn peername ssf connid +#socketpath /tmp/bind-listener +#sockops bind + +access to + dn.subtree="ou=data,dc=example,dc=org" + attrs=userPassword + by self =wx + by anonymous auth + by * none access to - dn.subtree="ou=realdb,dc=example,dc=org" + dn.subtree="ou=data,dc=example,dc=org" + attrs=entry + by self write by * read +access to + dn.subtree="ou=data,dc=example,dc=org" + by * read diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/examples/openldap/realdb.ldif new/slapdsock-0.5.3/examples/openldap/realdb.ldif --- old/slapdsock-0.5.2/examples/openldap/realdb.ldif 2016-04-22 22:37:55.000000000 +0200 +++ new/slapdsock-0.5.3/examples/openldap/realdb.ldif 2017-06-21 19:52:01.000000000 +0200 @@ -1,12 +1,16 @@ -dn: ou=realdb,dc=example,dc=org +dn: ou=data,dc=example,dc=org objectClass: organizationalUnit ou: realdb -dn: uid=test1,ou=realdb,dc=example,dc=org +dn: uid=test1,ou=data,dc=example,dc=org objectClass: account +objectClass: simpleSecurityObject uid: test1 +userPassword: testpw1 -dn: uid=test2,ou=realdb,dc=example,dc=org +dn: uid=test2,ou=data,dc=example,dc=org objectClass: account +objectClass: simpleSecurityObject uid: test2 +userPassword: testpw2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/examples/openldap/schema/init.schema new/slapdsock-0.5.3/examples/openldap/schema/init.schema --- old/slapdsock-0.5.2/examples/openldap/schema/init.schema 2017-03-20 10:39:03.000000000 +0100 +++ new/slapdsock-0.5.3/examples/openldap/schema/init.schema 1970-01-01 01:00:00.000000000 +0100 @@ -1,11 +0,0 @@ -################################################################## -# Erforderliche Schema-Definitionen -################################################################## - -# Schemata aus der OpenLDAP-Distribution -include /etc/openldap/schema/core.schema -include /etc/openldap/schema/cosine.schema -include /etc/openldap/schema/inetorgperson.schema -include /etc/openldap/schema/misc.schema -include /etc/openldap/schema/openldap.schema -include /etc/openldap/schema/ppolicy.schema diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/examples/openldap/slapd-sock-test.args new/slapdsock-0.5.3/examples/openldap/slapd-sock-test.args --- old/slapdsock-0.5.2/examples/openldap/slapd-sock-test.args 2017-03-20 10:43:49.000000000 +0100 +++ new/slapdsock-0.5.3/examples/openldap/slapd-sock-test.args 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -/usr/sbin/slapd -d stats,shell -h ldap://0.0.0.0:9876 ldapi://%2Fhome%2Fmichael%2FProj%2Fslapd_sockd%2Fexamples%2Fopenldap%2Fldapi -n slapd-sock-test -f slapd.conf diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/examples/openldap/slapd-sock-test.pid new/slapdsock-0.5.3/examples/openldap/slapd-sock-test.pid --- old/slapdsock-0.5.2/examples/openldap/slapd-sock-test.pid 2017-03-20 10:43:49.000000000 +0100 +++ new/slapdsock-0.5.3/examples/openldap/slapd-sock-test.pid 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -28352 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/examples/openldap/slapd.conf new/slapdsock-0.5.3/examples/openldap/slapd.conf --- old/slapdsock-0.5.2/examples/openldap/slapd.conf 2016-04-22 22:37:55.000000000 +0200 +++ new/slapdsock-0.5.3/examples/openldap/slapd.conf 2017-06-28 13:27:58.000000000 +0200 @@ -2,27 +2,38 @@ # Test configuration for demonstrating back-sock as database and overlay ############################################################################ -include schema/init.schema +# include standard OpenLDAP schema files +include /etc/openldap/schema/core.schema +include /etc/openldap/schema/cosine.schema +include /etc/openldap/schema/inetorgperson.schema +include /etc/openldap/schema/misc.schema +include /etc/openldap/schema/openldap.schema +include /etc/openldap/schema/ppolicy.schema #--------------------------------------------------------------------------- # Load dynamic backend modules #--------------------------------------------------------------------------- -moduleload back_mdb.la -moduleload back_sock.la -moduleload back_monitor.la +moduleload back_mdb.la +moduleload back_sock.la +moduleload back_monitor.la +moduleload back_ldap.la +moduleload back_relay.la #--------------------------------------------------------------------------- # Load dynamic overlay modules #--------------------------------------------------------------------------- +moduleload accesslog +moduleload ppolicy +moduleload rwm #--------------------------------------------------------------------------- # slapd global parameters #--------------------------------------------------------------------------- -#loglevel conns filter stats -threads 6 +#loglevel conns filter stats stats2 acl +threads 2 pidfile slapd-sock-test.pid argsfile slapd-sock-test.args @@ -31,6 +42,8 @@ modulepath /opt/openldap/lib64/openldap +password-hash {CLEARTEXT} +#password-hash {SSHA} #--------------------------------------------------------------------------- # Rewrite SASL identity to DIT identity @@ -39,22 +52,22 @@ # Only for local tests! authz-regexp "gidnumber=100\\+uidnumber=1000,cn=peercred,cn=external,cn=auth" - "cn=root,ou=realdb,dc=example,dc=org" + "cn=root,ou=data,dc=example,dc=org" # Map root user to rootdn when SASL/EXTERNAL is used with LDAPI authz-regexp "gidnumber=0\\+uidnumber=0,cn=peercred,cn=external,cn=auth" - "cn=root,ou=realdb,dc=example,dc=org" + "cn=root,ou=data,dc=example,dc=org" # EXTERNAL over Unix Domain socket authz-regexp "gidnumber=([0-9]+)\\+uidnumber=([0-9]+),cn=peercred,cn=external,cn=auth" - "ldap:///ou=realdb,dc=example,dc=org??sub?(&(objectClass=posixAccount)(uidNumber=$2)(gidNumber=$1))" + "ldap:///ou=data,dc=example,dc=org??sub?(&(objectClass=posixAccount)(uidNumber=$2)(gidNumber=$1))" # All mechs based on userID most times with password authz-regexp "uid=([a-zA-Z0-9]+),cn=(digest-md5|cram-md5|ntlm|plain|login|gssapi),cn=auth" - "ldap:///ou=realdb,dc=example,dc=org??sub?(uid=$1)" + "ldap:///ou=data,dc=example,dc=org??sub?(uid=$1)" #--------------------------------------------------------------------------- # Global access control @@ -74,7 +87,36 @@ access to dn.subtree="cn=config" - by dn.exact="cn=root,ou=realdb,dc=example,dc=org" read + by dn.exact="cn=root,ou=data,dc=example,dc=org" read + +####################################################################### +# Audit database cn=accesslog +####################################################################### + +database mdb + +suffix "cn=accesslog" +directory accesslog + +# Always set rootdn since needed by internally writing overlays +rootdn "cn=root,cn=accesslog" +# rootpw not needed in production! +#rootpw donotenable!!! + +sizelimit -1 + +# Index-Konfiguration +index reqStart eq +index reqType eq +index reqDN eq +index reqAuthzID eq +index reqResult eq +index reqEntryUUID eq +index objectClass eq + +access to + dn.subtree="cn=accesslog" + by * read #*************************************************************************** # Backend databases defined in external files diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/examples/openldap/sockdb.conf new/slapdsock-0.5.3/examples/openldap/sockdb.conf --- old/slapdsock-0.5.2/examples/openldap/sockdb.conf 2016-04-22 22:37:55.000000000 +0200 +++ new/slapdsock-0.5.3/examples/openldap/sockdb.conf 2017-06-21 19:53:01.000000000 +0200 @@ -12,11 +12,10 @@ sizelimit -1 -socketpath sockdb-listener +socketpath sock-listener extensions binddn peername ssf connid access to dn.subtree="ou=sock,dc=example,dc=org" - by dn.exact="cn=root,ou=realdb,dc=example,dc=org" write + by dn.exact="cn=root,ou=data,dc=example,dc=org" write by * read - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/examples/openldap/start-slapd.sh new/slapdsock-0.5.3/examples/openldap/start-slapd.sh --- old/slapdsock-0.5.2/examples/openldap/start-slapd.sh 2016-04-22 22:37:55.000000000 +0200 +++ new/slapdsock-0.5.3/examples/openldap/start-slapd.sh 2017-06-28 13:27:17.000000000 +0200 @@ -1,7 +1,7 @@ #!/bin/sh -#OPENLDAP_EXEC="/opt/openldap/lib64/slapd" -OPENLDAP_EXEC="/usr/sbin/slapd" +OPENLDAP_EXEC="/opt/openldap-RE24/lib64/slapd" +#OPENLDAP_EXEC="/usr/sbin/slapd" LOCALDIR="$(dirname $0)" PWD="$(pwd)" @@ -12,10 +12,10 @@ cd "${LOCALDIR}" -# -d config,stats,stats2,acl,args,trace,sync +# -d config,stats,stats2,acl,args,shell,trace,sync ${OPENLDAP_EXEC} \ - -d stats,shell \ + -d stats,shell,acl \ -h "ldap://0.0.0.0:9876 ${LDAPI_URI}" \ -n slapd-sock-test \ -f slapd.conf diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/examples/passmod.py new/slapdsock-0.5.3/examples/passmod.py --- old/slapdsock-0.5.2/examples/passmod.py 1970-01-01 01:00:00.000000000 +0100 +++ new/slapdsock-0.5.3/examples/passmod.py 2017-07-02 18:16:44.000000000 +0200 @@ -0,0 +1,466 @@ +#!/usr/bin/python -OO +# -*- coding: utf-8 -*- +""" +slapd-sock listener demon queried by OpenLDAP's slapd-sock + +this demon intercepts password changes in ADD and MODIFY operations +and exports the userPassword value +""" + +#----------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------- + +# from Python's standard lib +import datetime +import logging +import os +import sys + +# passlib +import passlib.context + +# from jwcrypto +#from jwcrypto.jwk import JWK +#from jwcrypto.jwe import JWE + +# python-ldap +import ldap +from ldap import LDAPError +from ldap.controls.simple import ProxyAuthzControl + +# local modules +from slapdsock.ldaphelper import ldap_datetime +from slapdsock.ldaphelper import MyLDAPUrl +from slapdsock.loghelper import combined_logger +from slapdsock.handler import SlapdSockHandler, SlapdSockHandlerError +from slapdsock.message import RESULTResponse + +# run multi-threaded +from slapdsock.service import SlapdSockThreadingServer + +#----------------------------------------------------------------------- +# Configuration constants +#----------------------------------------------------------------------- + +__version__ = '0.0.1' +__author__ = u'Michael Ströder <[email protected]>' + +# DN of default pwdPolicy entry used +# in case attribute pwdPolicySubentry is missing +#PWD_POLICY_SUBENTRY_DEFAULT = 'cn=ppolicy-default,cn=ae,ou=ae-dir' +PWD_POLICY_SUBENTRY_DEFAULT = None + +PWD_MIN_LENGTH = 0 +PWD_MIN_AGE = 0 +PWD_SCHEME = '{CRYPT}' +PWD_CRYPT_SCHEME = 'sha512_crypt' +PWD_CRYPT_SCHEME_ARGS = { + 'rounds': 5000, +} + +# UIDs and peer GIDS of peers which are granted access +# (list of int/strings) +ALLOWED_UIDS = [0, 'ldap', os.getuid()] +ALLOWED_GIDS = [0] + +# String with octal representation of socket permissions +SOCKET_PERMISSIONS = '0666' + +# Trace level for python-ldap logs +PYLDAP_TRACELEVEL = int(os.environ.get('PYLDAP_TRACELEVEL', 0)) + +# Number of times connecting to local LDAPI is retried before sending a +# failed response for a query +LDAP_MAXRETRYCOUNT = 10 +# Time to wait before retrying to connect within one query +LDAP_RETRYDELAY = 0.1 + +# SASL authz-ID to be sent along with SASL/EXTERNAL bind +#LDAP_SASL_AUTHZID = 'dn:uid=simple_bind_proxy,dc=example,dc=com' +LDAP_SASL_AUTHZID = None + +# Time in seconds for which normal LDAP searches will be valid in cache +LDAP_CACHE_TTL = 5.0 +# Time in seconds for which pwdPolicy and oathHOTPParams entries will be +# valid in cache +LDAP_LONG_CACHE_TTL = 20 * LDAP_CACHE_TTL + +# Timeout in seconds when connecting to local and remote LDAP servers +# used for ldap.OPT_NETWORK_TIMEOUT and ldap.OPT_TIMEOUT +LDAP_TIMEOUT = 3.0 + +# attribute containing username +LDAP_USERNAME_ATTR = 'uid' + +# Timeout in seconds for the server (Unix domain) socket +SOCKET_TIMEOUT = 2 * LDAP_TIMEOUT + +# Logging formats +SYS_LOG_FORMAT = '%(levelname)s %(message)s' +CONSOLE_LOG_FORMAT = '%(asctime)s %(levelname)s %(message)s' + +# Base number for floating average value of response delay +AVERAGE_COUNT = 100 + +# Default log level to use +LOG_LEVEL = int(os.environ.get('LOG_LEVEL', logging.INFO)) + +# Time (seconds) for assuming an userPassword+OTP value to be valid in cache +CACHE_TTL = -1.0 + +DEBUG_VARS = [ + 'pwd_changed_time', + 'pwd_policy_subentry_dn', + 'pwd_policy_subentry', + 'user_class', + 'user_entry', +] + +# Error messages +if __debug__: + DEBUG_VARS.extend([ + 'new_passwd', + ]) + +#----------------------------------------------------------------------- +# Classes and functions +#----------------------------------------------------------------------- + + +class PassModServer(SlapdSockThreadingServer): + + """ + This is used to pass in more parameters to the server instance + """ + ldapi_authz_id = LDAP_SASL_AUTHZID + ldap_retry_max = LDAP_MAXRETRYCOUNT + ldap_retry_delay = LDAP_RETRYDELAY + ldap_cache_ttl = LDAP_CACHE_TTL + + def __init__( + self, + server_address, + RequestHandlerClass, + logger, + average_count, + socket_timeout, + socket_permissions, + allowed_uids, + allowed_gids, + bind_and_activate=True, + log_vars=None, + ): + self._ldap_conn = None + SlapdSockThreadingServer.__init__( + self, + server_address, + RequestHandlerClass, + logger, + average_count, + socket_timeout, + socket_permissions, + allowed_uids, + allowed_gids, + bind_and_activate, + monitor_dn=None, + log_vars=log_vars, + ) + + +class PassModHandler(SlapdSockHandler): + + """ + Handler class which proxies some simple bind requests to remote server + """ + + def _read_user_entry(self, request): + # Try to read the user entry for the given request dn + try: + try: + local_ldap_conn = self.server.get_ldapi_conn() + ldap_result = local_ldap_conn.search_s( + request.dn.encode('utf-8'), + ldap.SCOPE_BASE, + '(objectClass=*)', + attrlist=[ + 'objectClass', + 'structuralObjectClass', + 'pwdChangedTime', + 'pwdPolicySubentry', + 'uid', + 'uidNumber', + ], + ) + except ldap.SERVER_DOWN, ldap_error: + self.server.disable_ldapi_conn() + raise ldap_error + except LDAPError, ldap_error: + raise SlapdSockHandlerError( + ldap_error, + log_level=logging.WARN, + response=RESULTResponse(request.msgid, ldap_error), + log_vars=self.server._log_vars, + ) + return ldap_result[0][1] # _read_user_entry() + + def _get_new_passwd(self, request): + """ + Try to extract userPassword from request + """ + for mod_op, mod_type, mod_vals in request.modops: + if mod_op in (ldap.MOD_REPLACE, ldap.MOD_ADD) and \ + (mod_type.lower() == 'userpassword' or mod_type == '2.5.4.35'): + if len(set(mod_vals)) != 1: + raise SlapdSockHandlerError( + '%d != 1 different userPassword values in %s for %r' % ( + len(set(mod_vals)), + request.__class__.__name__, + request.dn, + ), + log_level=logging.ERROR, + response='CONTINUE\n', + log_vars=self.server._log_vars, + ) + new_passwd = mod_vals[0] + if new_passwd.startswith(PWD_SCHEME): + raise SlapdSockHandlerError( + 'userPassword value already begins with %r' % PWD_SCHEME, + log_level=logging.DEBUG, + response='CONTINUE\n', + log_vars=self.server._log_vars, + ) + pw_context = passlib.context.CryptContext(schemes=[PWD_CRYPT_SCHEME]) + # save hashed password into request + mod_vals[0] = '{0}{1}'.format( + PWD_SCHEME, + pw_context.hash(new_passwd, **PWD_CRYPT_SCHEME_ARGS), + ) + return new_passwd + # nothing to do because there's no userPassword attribute in request + raise SlapdSockHandlerError( + 'No userPassword value in %s for %r' % ( + request.__class__.__name__, + request.dn, + ), + log_level=logging.DEBUG, + response='CONTINUE\n', + log_vars=self.server._log_vars, + ) + # end of _get_new_passwd() + + def _check_pwd_policy(self, request, new_passwd_len, pwd_policy_subentry_dn, pwd_changed_time): + if pwd_policy_subentry_dn is None: + self._log(logging.DEBUG, 'no password policy to check') + return + # Try to determine password policy + pwd_min_age = PWD_MIN_AGE + pwd_min_length = PWD_MIN_LENGTH + try: + try: + local_ldap_conn = self.server.get_ldapi_conn() + pwd_policy_subentry = local_ldap_conn.read_s( + pwd_policy_subentry_dn, + '(objectClass=pwdPolicy)', + attrlist=[ + 'pwdMinAge', + 'pwdMinLength', + ], + cache_time=LDAP_LONG_CACHE_TTL, + ) + except ldap.SERVER_DOWN, ldap_error: + self.server.disable_ldapi_conn() + raise ldap_error + except LDAPError, ldap_error: + raise SlapdSockHandlerError( + ldap_error, + log_level=logging.WARN, + response=RESULTResponse(request.msgid, ldap_error), + log_vars=self.server._log_vars, + ) + else: + pwd_min_age = int(pwd_policy_subentry.get('pwdMinAge', [PWD_MIN_AGE])[0]) + pwd_min_length = int(pwd_policy_subentry.get('pwdMinLength', [PWD_MIN_LENGTH])[0]) + # Check if minimum password length is ok + if new_passwd_len < pwd_min_length: + raise SlapdSockHandlerError( + 'Password for %r too short!' % (request.dn), + log_level=logging.INFO, + response=RESULTResponse( + request.msgid, + 'constraintViolation', + info='Password too short! Required minimum length is %d.' % ( + pwd_min_length + ), + ), + log_vars=self.server._log_vars, + ) + # Check if next password change is already allowed + if pwd_changed_time is not None and \ + (datetime.datetime.utcnow()-ldap_datetime(pwd_changed_time)).total_seconds < pwd_min_age: + raise SlapdSockHandlerError( + 'Password of %r too young to change!' % (request.dn), + log_level=logging.INFO, + response=RESULTResponse( + request.msgid, + 'constraintViolation', + info='Password too young to change!', + ), + log_vars=self.server._log_vars, + ) + return # end of _check_pwd_policy() + + def _export_password(self, request, user_entry, password): + """ + write reversible encrypted new password to sync queue + """ + try: + # all export actions which could fail goes here + _ = user_entry + self._log(logging.ERROR, 'Exported password: %r', password) + except Exception, err: + raise SlapdSockHandlerError( + 'Error exporting password of %r: %s' % (request.dn, err), + log_level=logging.ERROR, + response=RESULTResponse( + request.msgid, + 'operationsError', + info='export error', + ), + log_vars=self.server._log_vars, + ) + else: + self._log(logging.INFO, 'Exported password of %r', request.dn) + return # end of _export_password() + + def _update_user_entry(self, request): + """ + write modifications of request to LDAP entry + """ + try: + local_ldap_conn = self.server.get_ldapi_conn() + local_ldap_conn.modify_ext_s( + request.dn.encode('utf-8'), + request.modops, + serverctrls=[ + ProxyAuthzControl( + True, + 'dn:{0}'.format(request.binddn.encode('utf-8')), + ), + ], + ) + except ldap.LDAPError, ldap_error: + raise SlapdSockHandlerError( + 'LDAPError modifying entry %r: %s' % (request.dn, ldap_error), + log_level=logging.ERROR, + response=RESULTResponse(request.msgid, ldap_error), + log_vars=self.server._log_vars, + ) + else: + self._log( + logging.INFO, + 'Successfully modified userPassword for %r (from %r)', + request.dn, + request.peername, + ) + return # end of _update_user_entry() + + def do_modify(self, request): + """ + Handle MODIFY operation + """ + new_passwd = self._get_new_passwd(request) + user_entry = self._read_user_entry(request) + # Attributes from user entry + #user_class = user_entry.get('structuralObjectClass', [None])[0] + pwd_changed_time = user_entry.get('pwdChangedTime', [None])[0] + pwd_policy_subentry_dn = user_entry.get( + 'pwdPolicySubentry', + [PWD_POLICY_SUBENTRY_DEFAULT] + )[0] + self._check_pwd_policy( + request, + len(new_passwd), + pwd_policy_subentry_dn, + pwd_changed_time + ) + self._update_user_entry(request) + self._export_password(request, user_entry, new_passwd) + return RESULTResponse(request.msgid, 'success') # end of do_modify() + + +#----------------------------------------------------------------------- +# Main +#----------------------------------------------------------------------- + +def run_this(): + """ + The main script + """ + + script_name = os.path.abspath(sys.argv[0]) + + log_level = LOG_LEVEL + console_log_format = None + if __debug__ and os.environ.get('DEBUG', 'no') == 'yes': + log_level = logging.DEBUG + console_log_format = CONSOLE_LOG_FORMAT + + my_logger = combined_logger( + os.path.basename(script_name), + log_level, + sys_log_format=SYS_LOG_FORMAT, + console_log_format=console_log_format, + ) + + my_logger.info( + 'Starting %s %s (log level %d)', + script_name, + __version__, + my_logger.level + ) + + if __debug__: + my_logger.error( + '!!! Running in debug mode (log level %d)! ' + 'Secret data will be logged! Don\'t do that!!!', + my_logger.level + ) + + try: + socket_path = sys.argv[1] + local_ldap_uri = sys.argv[2] + except IndexError: + my_logger.error('Not enough arguments => abort') + sys.exit(1) + + local_ldap_uri_obj = MyLDAPUrl(local_ldap_uri) + + try: + slapd_sock_listener = PassModServer( + socket_path, + PassModHandler, + my_logger, + AVERAGE_COUNT, + SOCKET_TIMEOUT, SOCKET_PERMISSIONS, + ALLOWED_UIDS, ALLOWED_GIDS, + log_vars=DEBUG_VARS, + ) + slapd_sock_listener.ldapi_uri = local_ldap_uri_obj.initializeUrl() + slapd_sock_listener.ldap_trace_level = PYLDAP_TRACELEVEL + try: + slapd_sock_listener.serve_forever() + except KeyboardInterrupt: + my_logger.warn('Received interrupt signal => shutdown') + finally: + my_logger.debug('Remove socket path %s', repr(socket_path)) + try: + os.remove(socket_path) + except OSError: + pass + + return # end of main() + + +if __name__ == '__main__': + run_this() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/slapdsock/message.py new/slapdsock-0.5.3/slapdsock/message.py --- old/slapdsock-0.5.2/slapdsock/message.py 2017-03-20 11:23:32.000000000 +0100 +++ new/slapdsock-0.5.3/slapdsock/message.py 2017-07-02 00:43:16.000000000 +0200 @@ -39,6 +39,7 @@ # from python-ldap import ldif from ldif import LDIFWriter +from ldap import LDAPError from slapdsock.ldaphelper import RESULT_CODE @@ -362,13 +363,17 @@ """ req_attrs = { 'msgid': int, + 'binddn': ldap_str, + 'peername': ldap_str, + 'ssf': int, + 'connid': int, 'code': int, 'matched': ldap_str, 'info': ldap_str, } -class ENTRYRequest(SockRequest): +class ENTRYRequest(ADDRequest): """ ENTRY @@ -378,6 +383,10 @@ """ req_attrs = { 'msgid': int, + 'binddn': ldap_str, + 'peername': ldap_str, + 'ssf': int, + 'connid': int, } @@ -468,15 +477,32 @@ <blank line> """ - def __init__(self, msgid, code=0, matched=None, info=None): - msgid = msgid or 0 - if isinstance(code, basestring): - code = RESULT_CODE[code] + def __init__(self, msgid, code, matched=None, info=None): assert type(msgid) == IntType, ValueError( - 'Argument msgid must be integer but was: %s' % repr(msgid) + 'Argument msgid must be integer but was: %r' % msgid ) + msgid = msgid or 0 + if isinstance(code, int): + code = code + elif isinstance(code, basestring): + # result code specified by name + code = RESULT_CODE[code] + elif isinstance(code, LDAPError): + # clone response args from LDAPError instance + ldap_error = code + code = RESULT_CODE.get(type(ldap_error), RESULT_CODE['other']) + try: + info = ldap_error.args[0]['info'].decode('utf-8') + except (AttributeError, KeyError): + pass + try: + matched = ldap_error.args[0]['matched'].decode('utf-8') + except (AttributeError, KeyError): + pass + else: + raise TypeError('Invalid type of argument code=%r' % (code)) assert type(code) == IntType, ValueError( - 'Argument code must be integer but was: %s' % repr(code) + 'Argument code must be integer but was: %r' % code ) resp_lines = [ #('msgid', str(msgid)), @@ -534,7 +560,7 @@ class InternalErrorResponse(RESULTResponse): """ - Convenience wrapper class for returning unwillingToPerform and + Convenience wrapper class for returning unwillingToPerform and diagnostic message 'internal error' """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/slapdsock/pkginfo.py new/slapdsock-0.5.3/slapdsock/pkginfo.py --- old/slapdsock-0.5.2/slapdsock/pkginfo.py 2017-03-20 11:37:15.000000000 +0100 +++ new/slapdsock-0.5.3/slapdsock/pkginfo.py 2017-07-01 20:06:32.000000000 +0200 @@ -2,6 +2,6 @@ """ meta attributes for packaging which does not import any dependencies """ -__version__ = '0.5.2' +__version__ = '0.5.3' __author__ = u'Michael Ströder <[email protected]>' __license__ = 'Apache License, Version 2.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/slapdsock.egg-info/PKG-INFO new/slapdsock-0.5.3/slapdsock.egg-info/PKG-INFO --- old/slapdsock-0.5.2/slapdsock.egg-info/PKG-INFO 2017-03-20 11:45:10.000000000 +0100 +++ new/slapdsock-0.5.3/slapdsock.egg-info/PKG-INFO 2017-07-02 18:18:43.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: slapdsock -Version: 0.5.2 +Version: 0.5.3 Summary: Module package for back-sock listeners for OpenLDAP Home-page: https://www.stroeder.com/slapdsock.html Author: Michael Stroeder diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/slapdsock.egg-info/SOURCES.txt new/slapdsock-0.5.3/slapdsock.egg-info/SOURCES.txt --- old/slapdsock-0.5.2/slapdsock.egg-info/SOURCES.txt 2017-03-20 11:45:10.000000000 +0100 +++ new/slapdsock-0.5.3/slapdsock.egg-info/SOURCES.txt 2017-07-02 18:18:43.000000000 +0200 @@ -4,16 +4,17 @@ setup.cfg setup.py examples/noop_logger.py +examples/passmod.py examples/simple_bind_demo.py examples/openldap/realdb.conf examples/openldap/realdb.ldif -examples/openldap/slapd-sock-test.args -examples/openldap/slapd-sock-test.pid examples/openldap/slapd.conf examples/openldap/sockdb.conf examples/openldap/start-slapd.sh +examples/openldap/accesslog/.placeholder +examples/openldap/accesslog/data.mdb +examples/openldap/accesslog/lock.mdb examples/openldap/realdb/.placeholder -examples/openldap/schema/init.schema slapdsock/__init__.py slapdsock/cache.py slapdsock/handler.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/slapdsock-0.5.2/tests/t_message.py new/slapdsock-0.5.3/tests/t_message.py --- old/slapdsock-0.5.2/tests/t_message.py 2017-03-20 11:25:37.000000000 +0100 +++ new/slapdsock-0.5.3/tests/t_message.py 2017-06-21 21:26:13.000000000 +0200 @@ -180,6 +180,14 @@ def test_modify_userpassword(self): req = MODIFYRequest(self.prepare_request_msg(modify_message_userpassword)) + self.assertEquals(req.reqtype, u'MODIFY') + self.assertEquals(req.msgid, 2) + self.assertEquals(req.binddn, u'cn=root,ou=realdb,dc=example,dc=org') + self.assertEquals(req.peername, u'PATH=/tmp/slapd_sockd/examples/openldap/ldapi') + self.assertEquals(req.ssf, 71) + self.assertEquals(req.connid, 1004) + self.assertEquals(req.dn, u'uid=test1,ou=realdb,dc=example,dc=org') + self.assertEquals(req.modops, [(2, 'userPassword', ['{SSHA}5/WxVWC6CtRetVnVNP3Sv5s4F/kQRURR'])]) if __name__ == '__main__':
