mirror of
https://github.com/GAM-team/GAM.git
synced 2025-05-12 12:17:20 +00:00
220 lines
8.3 KiB
Python
220 lines
8.3 KiB
Python
"""passlib.handlers.cisco - Cisco password hashes"""
|
|
#=============================================================================
|
|
# imports
|
|
#=============================================================================
|
|
# core
|
|
from binascii import hexlify, unhexlify
|
|
from hashlib import md5
|
|
import logging; log = logging.getLogger(__name__)
|
|
from warnings import warn
|
|
# site
|
|
# pkg
|
|
from passlib.utils import h64, right_pad_string, to_unicode
|
|
from passlib.utils.compat import b, bascii_to_str, bytes, unicode, u, join_byte_values, \
|
|
join_byte_elems, byte_elem_value, iter_byte_values, uascii_to_str, str_to_uascii
|
|
import passlib.utils.handlers as uh
|
|
# local
|
|
__all__ = [
|
|
"cisco_pix",
|
|
"cisco_type7",
|
|
]
|
|
|
|
#=============================================================================
|
|
# cisco pix firewall hash
|
|
#=============================================================================
|
|
class cisco_pix(uh.HasUserContext, uh.StaticHandler):
|
|
"""This class implements the password hash used by Cisco PIX firewalls,
|
|
and follows the :ref:`password-hash-api`.
|
|
It does a single round of hashing, and relies on the username
|
|
as the salt.
|
|
|
|
The :meth:`~passlib.ifc.PasswordHash.encrypt`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods
|
|
have the following extra keyword:
|
|
|
|
:type user: str
|
|
:param user:
|
|
String containing name of user account this password is associated with.
|
|
|
|
This is *required* in order to correctly hash passwords associated
|
|
with a user account on the Cisco device, as it is used to salt
|
|
the hash.
|
|
|
|
Conversely, this *must* be omitted or set to ``""`` in order to correctly
|
|
hash passwords which don't have an associated user account
|
|
(such as the "enable" password).
|
|
"""
|
|
#===================================================================
|
|
# class attrs
|
|
#===================================================================
|
|
name = "cisco_pix"
|
|
checksum_size = 16
|
|
checksum_chars = uh.HASH64_CHARS
|
|
|
|
#===================================================================
|
|
# methods
|
|
#===================================================================
|
|
def _calc_checksum(self, secret):
|
|
if isinstance(secret, unicode):
|
|
# XXX: no idea what unicode policy is, but all examples are
|
|
# 7-bit ascii compatible, so using UTF-8
|
|
secret = secret.encode("utf-8")
|
|
|
|
user = self.user
|
|
if user:
|
|
# not positive about this, but it looks like per-user
|
|
# accounts use the first 4 chars of the username as the salt,
|
|
# whereas global "enable" passwords don't have any salt at all.
|
|
if isinstance(user, unicode):
|
|
user = user.encode("utf-8")
|
|
secret += user[:4]
|
|
|
|
# null-pad or truncate to 16 bytes
|
|
secret = right_pad_string(secret, 16)
|
|
|
|
# md5 digest
|
|
hash = md5(secret).digest()
|
|
|
|
# drop every 4th byte
|
|
hash = join_byte_elems(c for i,c in enumerate(hash) if i & 3 < 3)
|
|
|
|
# encode using Hash64
|
|
return h64.encode_bytes(hash).decode("ascii")
|
|
|
|
#===================================================================
|
|
# eoc
|
|
#===================================================================
|
|
|
|
#=============================================================================
|
|
# type 7
|
|
#=============================================================================
|
|
class cisco_type7(uh.GenericHandler):
|
|
"""This class implements the Type 7 password encoding used by Cisco IOS,
|
|
and follows the :ref:`password-hash-api`.
|
|
It has a simple 4-5 bit salt, but is nonetheless a reversible encoding
|
|
instead of a real hash.
|
|
|
|
The :meth:`~passlib.ifc.PasswordHash.encrypt` and :meth:`~passlib.ifc.PasswordHash.genhash` methods
|
|
have the following optional keywords:
|
|
|
|
:type salt: int
|
|
:param salt:
|
|
This may be an optional salt integer drawn from ``range(0,16)``.
|
|
If omitted, one will be chosen at random.
|
|
|
|
: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`` values that are out of range.
|
|
|
|
Note that while this class outputs digests in upper-case hexidecimal,
|
|
it will accept lower-case as well.
|
|
|
|
This class also provides the following additional method:
|
|
|
|
.. automethod:: decode
|
|
"""
|
|
#===================================================================
|
|
# class attrs
|
|
#===================================================================
|
|
name = "cisco_type7"
|
|
setting_kwds = ("salt",)
|
|
checksum_chars = uh.UPPER_HEX_CHARS
|
|
|
|
# NOTE: encoding could handle max_salt_value=99, but since key is only 52
|
|
# chars in size, not sure what appropriate behavior is for that edge case.
|
|
min_salt_value = 0
|
|
max_salt_value = 52
|
|
|
|
#===================================================================
|
|
# methods
|
|
#===================================================================
|
|
@classmethod
|
|
def genconfig(cls):
|
|
return None
|
|
|
|
@classmethod
|
|
def genhash(cls, secret, config):
|
|
# special case to handle ``config=None`` in same style as StaticHandler
|
|
if config is None:
|
|
return cls.encrypt(secret)
|
|
else:
|
|
return super(cisco_type7, cls).genhash(secret, config)
|
|
|
|
@classmethod
|
|
def from_string(cls, hash):
|
|
hash = to_unicode(hash, "ascii", "hash")
|
|
if len(hash) < 2:
|
|
raise uh.exc.InvalidHashError(cls)
|
|
salt = int(hash[:2]) # may throw ValueError
|
|
return cls(salt=salt, checksum=hash[2:].upper())
|
|
|
|
def __init__(self, salt=None, **kwds):
|
|
super(cisco_type7, self).__init__(**kwds)
|
|
self.salt = self._norm_salt(salt)
|
|
|
|
def _norm_salt(self, salt):
|
|
"the salt for this algorithm is an integer 0-52, not a string"
|
|
# XXX: not entirely sure that values >15 are valid, so for
|
|
# compatibility we don't output those values, but we do accept them.
|
|
if salt is None:
|
|
if self.use_defaults:
|
|
salt = self._generate_salt()
|
|
else:
|
|
raise TypeError("no salt specified")
|
|
if not isinstance(salt, int):
|
|
raise uh.exc.ExpectedTypeError(salt, "integer", "salt")
|
|
if salt < 0 or salt > self.max_salt_value:
|
|
msg = "salt/offset must be in 0..52 range"
|
|
if self.relaxed:
|
|
warn(msg, uh.PasslibHashWarning)
|
|
salt = 0 if salt < 0 else self.max_salt_value
|
|
else:
|
|
raise ValueError(msg)
|
|
return salt
|
|
|
|
def _generate_salt(self):
|
|
return uh.rng.randint(0, 15)
|
|
|
|
def to_string(self):
|
|
return "%02d%s" % (self.salt, uascii_to_str(self.checksum))
|
|
|
|
def _calc_checksum(self, secret):
|
|
# XXX: no idea what unicode policy is, but all examples are
|
|
# 7-bit ascii compatible, so using UTF-8
|
|
if isinstance(secret, unicode):
|
|
secret = secret.encode("utf-8")
|
|
return hexlify(self._cipher(secret, self.salt)).decode("ascii").upper()
|
|
|
|
@classmethod
|
|
def decode(cls, hash, encoding="utf-8"):
|
|
"""decode hash, returning original password.
|
|
|
|
:arg hash: encoded password
|
|
:param encoding: optional encoding to use (defaults to ``UTF-8``).
|
|
:returns: password as unicode
|
|
"""
|
|
self = cls.from_string(hash)
|
|
tmp = unhexlify(self.checksum.encode("ascii"))
|
|
raw = self._cipher(tmp, self.salt)
|
|
return raw.decode(encoding) if encoding else raw
|
|
|
|
# type7 uses a xor-based vingere variant, using the following secret key:
|
|
_key = u("dsfd;kfoA,.iyewrkldJKDHSUBsgvca69834ncxv9873254k;fg87")
|
|
|
|
@classmethod
|
|
def _cipher(cls, data, salt):
|
|
"xor static key against data - encrypts & decrypts"
|
|
key = cls._key
|
|
key_size = len(key)
|
|
return join_byte_values(
|
|
value ^ ord(key[(salt + idx) % key_size])
|
|
for idx, value in enumerate(iter_byte_values(data))
|
|
)
|
|
|
|
#=============================================================================
|
|
# eof
|
|
#=============================================================================
|