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
|
"""passlib.handlers.misc - misc generic handlers
"""
#=========================================================
#imports
#=========================================================
#core
import sys
import logging; log = logging.getLogger(__name__)
from warnings import warn
#site
#libs
from passlib.utils import to_native_str, consteq
from passlib.utils.compat import bytes, unicode, u, base_string_types
import passlib.utils.handlers as uh
#pkg
#local
__all__ = [
"unix_disabled",
"unix_fallback",
"plaintext",
]
#=========================================================
#handler
#=========================================================
class unix_fallback(uh.StaticHandler):
"""This class provides the fallback behavior for unix shadow files, and follows the :ref:`password-hash-api`.
.. note::
This class has been deprecated as of Passlib 1.6,
and will be removed in Passlib 1.8.
Use 'unix_disabled' instead.
This class does not implement a hash, but instead provides fallback
behavior as found in /etc/shadow on most unix variants.
If used, should be the last scheme in the context.
* this class will positive identify all hash strings.
* for security, newly encrypted passwords will hash to ``!``.
* it rejects all passwords if the hash is NOT an empty string (``!`` or ``*`` are frequently used).
* by default it rejects all passwords if the hash is an empty string,
but if ``enable_wildcard=True`` is passed to verify(),
all passwords will be allowed through if the hash is an empty string.
"""
name = "unix_fallback"
context_kwds = ("enable_wildcard",)
@classmethod
def identify(cls, hash):
if isinstance(hash, base_string_types):
return True
else:
raise uh.exc.ExpectedStringError(hash, "hash")
def __init__(self, enable_wildcard=False, **kwds):
warn("'unix_fallback' is deprecated, "
"and will be removed in Passlib 1.8; "
"please use 'unix_disabled' instead.",
DeprecationWarning)
super(unix_fallback, self).__init__(**kwds)
self.enable_wildcard = enable_wildcard
def _calc_checksum(self, secret):
if self.checksum:
# NOTE: hash will generally be "!", but we want to preserve
# it in case it's something else, like "*".
return self.checksum
else:
return u("!")
@classmethod
def verify(cls, secret, hash, enable_wildcard=False):
uh.validate_secret(secret)
if not isinstance(hash, base_string_types):
raise uh.exc.ExpectedStringError(hash, "hash")
elif hash:
return False
else:
return enable_wildcard
class unix_disabled(object):
"""This class provides disabled password behavior for unix shadow files,
and follows the :ref:`password-hash-api`. This class does not implement a
hash, but instead provides disabled account behavior as found in
``/etc/shadow`` on most unix variants.
* this class will positively identify all hash strings.
because of this it should be checked last.
* "encrypting" a password will simply return the disabled account marker.
* it will reject all passwords, no matter the hash.
The :meth:`encrypt` method supports one optional keyword:
:param marker:
Optional marker string which overrides the platform default
used to indicate a disabled account.
If not specified, this will default to ``*`` on BSD systems,
and use the Linux default ``!`` for all other platforms.
(:attr:`!unix_disabled.marker` will contain the default value)
"""
name = "unix_disabled"
setting_kwds = ("marker",)
context_kwds = ()
if 'bsd' in sys.platform:
marker = u("*")
else:
# use the linux default for other systems
# (glibc also supports adding old hash after the marker
# so it can be restored later).
marker = u("!")
@classmethod
def identify(cls, hash):
if isinstance(hash, base_string_types):
return True
else:
raise uh.exc.ExpectedStringError(hash, "hash")
@classmethod
def encrypt(cls, secret, marker=None):
return cls.genhash(secret, None, marker)
@classmethod
def verify(cls, secret, hash):
uh.validate_secret(secret)
if not isinstance(hash, base_string_types):
raise uh.exc.ExpectedStringError(hash, "hash")
return False
@classmethod
def genconfig(cls):
return None
@classmethod
def genhash(cls, secret, config, marker=None):
uh.validate_secret(secret)
if config is not None:
# NOTE: config/hash will generally be "!" or "*",
# but we want to preserve it in case it has some other content,
# such as ``"!" + original hash``, which glibc uses.
# XXX: should this detect mcf header, or other things re:
# local system policy?
return to_native_str(config, errname="config")
else:
return to_native_str(marker or cls.marker, errname="marker")
class plaintext(object):
"""This class stores passwords in plaintext, and follows the :ref:`password-hash-api`.
Unicode passwords will be encoded using utf-8.
Under Python 3, existing 'hashes' must decode as utf-8.
"""
# NOTE: this tries to avoid decoding bytes under py2,
# for applications that are using latin-1 or some other encoding.
# they'll just have to stop using plaintext under py3 :)
# (or re-encode as utf-8)
# NOTE: this is subclassed by ldap_plaintext
name = "plaintext"
setting_kwds = ()
context_kwds = ()
_hash_encoding = "utf-8"
@classmethod
def identify(cls, hash):
if isinstance(hash, base_string_types):
return True
else:
raise uh.exc.ExpectedStringError(hash, "hash")
@classmethod
def encrypt(cls, secret):
uh.validate_secret(secret)
return to_native_str(secret, cls._hash_encoding, "secret")
@classmethod
def verify(cls, secret, hash):
hash = to_native_str(hash, cls._hash_encoding, "hash")
if not cls.identify(hash):
raise uh.exc.InvalidHashError(cls)
return consteq(cls.encrypt(secret), hash)
@classmethod
def genconfig(cls):
return None
@classmethod
def genhash(cls, secret, hash):
if hash is not None and not cls.identify(hash):
raise uh.exc.InvalidHashError(cls)
return cls.encrypt(secret)
#=========================================================
#eof
#=========================================================
|