/usr/lib/python2.7/dist-packages/passlib/handlers/ldap_digests.py is in python-passlib 1.7.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | """passlib.handlers.digests - plain hash digests
"""
#=============================================================================
# imports
#=============================================================================
# core
from base64 import b64encode, b64decode
from hashlib import md5, sha1
import logging; log = logging.getLogger(__name__)
import re
# site
# pkg
from passlib.handlers.misc import plaintext
from passlib.utils import unix_crypt_schemes, to_unicode
from passlib.utils.compat import uascii_to_str, unicode, u
from passlib.utils.decor import classproperty
import passlib.utils.handlers as uh
# local
__all__ = [
"ldap_plaintext",
"ldap_md5",
"ldap_sha1",
"ldap_salted_md5",
"ldap_salted_sha1",
##"get_active_ldap_crypt_schemes",
"ldap_des_crypt",
"ldap_bsdi_crypt",
"ldap_md5_crypt",
"ldap_sha1_crypt"
"ldap_bcrypt",
"ldap_sha256_crypt",
"ldap_sha512_crypt",
]
#=============================================================================
# ldap helpers
#=============================================================================
class _Base64DigestHelper(uh.StaticHandler):
"""helper for ldap_md5 / ldap_sha1"""
# XXX: could combine this with hex digests in digests.py
ident = None # required - prefix identifier
_hash_func = None # required - hash function
_hash_regex = None # required - regexp to recognize hash
checksum_chars = uh.PADDED_BASE64_CHARS
@classproperty
def _hash_prefix(cls):
"""tell StaticHandler to strip ident from checksum"""
return cls.ident
def _calc_checksum(self, secret):
if isinstance(secret, unicode):
secret = secret.encode("utf-8")
chk = self._hash_func(secret).digest()
return b64encode(chk).decode("ascii")
class _SaltedBase64DigestHelper(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
"""helper for ldap_salted_md5 / ldap_salted_sha1"""
setting_kwds = ("salt", "salt_size")
checksum_chars = uh.PADDED_BASE64_CHARS
ident = None # required - prefix identifier
_hash_func = None # required - hash function
_hash_regex = None # required - regexp to recognize hash
min_salt_size = max_salt_size = 4
# NOTE: openldap implementation uses 4 byte salt,
# but it's been reported (issue 30) that some servers use larger salts.
# the semi-related rfc3112 recommends support for up to 16 byte salts.
min_salt_size = 4
default_salt_size = 4
max_salt_size = 16
@classmethod
def from_string(cls, hash):
hash = to_unicode(hash, "ascii", "hash")
m = cls._hash_regex.match(hash)
if not m:
raise uh.exc.InvalidHashError(cls)
try:
data = b64decode(m.group("tmp").encode("ascii"))
except TypeError:
raise uh.exc.MalformedHashError(cls)
cs = cls.checksum_size
assert cs
return cls(checksum=data[:cs], salt=data[cs:])
def to_string(self):
data = self.checksum + self.salt
hash = self.ident + b64encode(data).decode("ascii")
return uascii_to_str(hash)
def _calc_checksum(self, secret):
if isinstance(secret, unicode):
secret = secret.encode("utf-8")
return self._hash_func(secret + self.salt).digest()
#=============================================================================
# implementations
#=============================================================================
class ldap_md5(_Base64DigestHelper):
"""This class stores passwords using LDAP's plain MD5 format, and follows the :ref:`password-hash-api`.
The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods have no optional keywords.
"""
name = "ldap_md5"
ident = u("{MD5}")
_hash_func = md5
_hash_regex = re.compile(u(r"^\{MD5\}(?P<chk>[+/a-zA-Z0-9]{22}==)$"))
class ldap_sha1(_Base64DigestHelper):
"""This class stores passwords using LDAP's plain SHA1 format, and follows the :ref:`password-hash-api`.
The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods have no optional keywords.
"""
name = "ldap_sha1"
ident = u("{SHA}")
_hash_func = sha1
_hash_regex = re.compile(u(r"^\{SHA\}(?P<chk>[+/a-zA-Z0-9]{27}=)$"))
class ldap_salted_md5(_SaltedBase64DigestHelper):
"""This class stores passwords using LDAP's salted MD5 format, and follows the :ref:`password-hash-api`.
It supports a 4-16 byte salt.
The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
:type salt: bytes
:param salt:
Optional salt string.
If not specified, one will be autogenerated (this is recommended).
If specified, it may be any 4-16 byte string.
:type salt_size: int
:param salt_size:
Optional number of bytes to use when autogenerating new salts.
Defaults to 4 bytes for compatibility with the LDAP spec,
but some systems use larger salts, and Passlib supports
any value between 4-16.
:type relaxed: bool
:param relaxed:
By default, providing an invalid value for one of the other
keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
will be issued instead. Correctable errors include
``salt`` strings that are too long.
.. versionadded:: 1.6
.. versionchanged:: 1.6
This format now supports variable length salts, instead of a fix 4 bytes.
"""
name = "ldap_salted_md5"
ident = u("{SMD5}")
checksum_size = 16
_hash_func = md5
_hash_regex = re.compile(u(r"^\{SMD5\}(?P<tmp>[+/a-zA-Z0-9]{27,}={0,2})$"))
class ldap_salted_sha1(_SaltedBase64DigestHelper):
"""This class stores passwords using LDAP's salted SHA1 format, and follows the :ref:`password-hash-api`.
It supports a 4-16 byte salt.
The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
:type salt: bytes
:param salt:
Optional salt string.
If not specified, one will be autogenerated (this is recommended).
If specified, it may be any 4-16 byte string.
:type salt_size: int
:param salt_size:
Optional number of bytes to use when autogenerating new salts.
Defaults to 4 bytes for compatibility with the LDAP spec,
but some systems use larger salts, and Passlib supports
any value between 4-16.
:type relaxed: bool
:param relaxed:
By default, providing an invalid value for one of the other
keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
will be issued instead. Correctable errors include
``salt`` strings that are too long.
.. versionadded:: 1.6
.. versionchanged:: 1.6
This format now supports variable length salts, instead of a fix 4 bytes.
"""
name = "ldap_salted_sha1"
ident = u("{SSHA}")
checksum_size = 20
_hash_func = sha1
_hash_regex = re.compile(u(r"^\{SSHA\}(?P<tmp>[+/a-zA-Z0-9]{32,}={0,2})$"))
class ldap_plaintext(plaintext):
"""This class stores passwords in plaintext, and follows the :ref:`password-hash-api`.
This class acts much like the generic :class:`!passlib.hash.plaintext` handler,
except that it will identify a hash only if it does NOT begin with the ``{XXX}`` identifier prefix
used by RFC2307 passwords.
The :meth:`~passlib.ifc.PasswordHash.hash`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods all require the
following additional contextual keyword:
:type encoding: str
:param encoding:
This controls the character encoding to use (defaults to ``utf-8``).
This encoding will be used to encode :class:`!unicode` passwords
under Python 2, and decode :class:`!bytes` hashes under Python 3.
.. versionchanged:: 1.6
The ``encoding`` keyword was added.
"""
# NOTE: this subclasses plaintext, since all it does differently
# is override identify()
name = "ldap_plaintext"
_2307_pat = re.compile(u(r"^\{\w+\}.*$"))
@uh.deprecated_method(deprecated="1.7", removed="2.0")
@classmethod
def genconfig(cls):
# Overridding plaintext.genconfig() since it returns "",
# but have to return non-empty value due to identify() below
return "!"
@classmethod
def identify(cls, hash):
# NOTE: identifies all strings EXCEPT those with {XXX} prefix
hash = uh.to_unicode_for_identify(hash)
return bool(hash) and cls._2307_pat.match(hash) is None
#=============================================================================
# {CRYPT} wrappers
# the following are wrappers around the base crypt algorithms,
# which add the ldap required {CRYPT} prefix
#=============================================================================
ldap_crypt_schemes = [ 'ldap_' + name for name in unix_crypt_schemes ]
def _init_ldap_crypt_handlers():
# NOTE: I don't like to implicitly modify globals() like this,
# but don't want to write out all these handlers out either :)
g = globals()
for wname in unix_crypt_schemes:
name = 'ldap_' + wname
g[name] = uh.PrefixWrapper(name, wname, prefix=u("{CRYPT}"), lazy=True)
del g
_init_ldap_crypt_handlers()
##_lcn_host = None
##def get_host_ldap_crypt_schemes():
## global _lcn_host
## if _lcn_host is None:
## from passlib.hosts import host_context
## schemes = host_context.schemes()
## _lcn_host = [
## "ldap_" + name
## for name in unix_crypt_names
## if name in schemes
## ]
## return _lcn_host
#=============================================================================
# eof
#=============================================================================
|