"Fossies" - the Fresh Open Source Software Archive

Member "salt-3002.2/salt/utils/pycrypto.py" (18 Nov 2020, 4684 Bytes) of package /linux/misc/salt-3002.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "pycrypto.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3002.1_vs_3002.2.

    1 """
    2 Use pycrypto to generate random passwords on the fly.
    3 """
    4 
    5 # Import python libraries
    6 
    7 import logging
    8 import random
    9 import re
   10 import string
   11 
   12 import salt.utils.stringutils
   13 from salt.exceptions import CommandExecutionError, SaltInvocationError
   14 
   15 try:
   16     try:
   17         from M2Crypto.Rand import rand_bytes as get_random_bytes
   18     except ImportError:
   19         try:
   20             from Cryptodome.Random import get_random_bytes  # pylint: disable=E0611
   21         except ImportError:
   22             from Crypto.Random import get_random_bytes  # pylint: disable=E0611
   23     HAS_RANDOM = True
   24 except ImportError:
   25     HAS_RANDOM = False
   26 
   27 try:
   28     import crypt
   29 
   30     HAS_CRYPT = True
   31 except ImportError:
   32     HAS_CRYPT = False
   33 
   34 try:
   35     import passlib.context
   36 
   37     HAS_PASSLIB = True
   38 except ImportError:
   39     HAS_PASSLIB = False
   40 
   41 log = logging.getLogger(__name__)
   42 
   43 
   44 def secure_password(length=20, use_random=True):
   45     """
   46     Generate a secure password.
   47     """
   48     try:
   49         length = int(length)
   50         pw = ""
   51         while len(pw) < length:
   52             if HAS_RANDOM and use_random:
   53                 while True:
   54                     try:
   55                         char = salt.utils.stringutils.to_str(get_random_bytes(1))
   56                         break
   57                     except UnicodeDecodeError:
   58                         continue
   59                 pw += re.sub(
   60                     salt.utils.stringutils.to_str(r"[\W_]"),
   61                     "",  # future lint: disable=blacklisted-function
   62                     char,
   63                 )
   64             else:
   65                 pw += random.SystemRandom().choice(string.ascii_letters + string.digits)
   66         return pw
   67     except Exception as exc:  # pylint: disable=broad-except
   68         log.exception("Failed to generate secure passsword")
   69         raise CommandExecutionError(str(exc))
   70 
   71 
   72 if HAS_CRYPT:
   73     methods = {m.name.lower(): m for m in crypt.methods}
   74 else:
   75     methods = {}
   76 known_methods = ["sha512", "sha256", "blowfish", "md5", "crypt"]
   77 
   78 
   79 def _gen_hash_passlib(crypt_salt=None, password=None, algorithm=None):
   80     """
   81     Generate a /etc/shadow-compatible hash for a non-local system
   82     """
   83     # these are the passlib equivalents to the 'known_methods' defined in crypt
   84     schemes = ["sha512_crypt", "sha256_crypt", "bcrypt", "md5_crypt", "des_crypt"]
   85 
   86     ctx = passlib.context.CryptContext(schemes=schemes)
   87 
   88     kwargs = {"secret": password, "scheme": schemes[known_methods.index(algorithm)]}
   89     if crypt_salt and "$" in crypt_salt:
   90         # this salt has a rounds specifier.
   91         #  passlib takes it as a separate parameter, split it out
   92         roundsstr, split_salt = crypt_salt.split("$")
   93         rounds = int(roundsstr.split("=")[-1])
   94         kwargs.update({"salt": split_salt, "rounds": rounds})
   95     else:
   96         # relaxed = allow salts that are too long
   97         kwargs.update({"salt": crypt_salt, "relaxed": True})
   98     return ctx.hash(**kwargs)
   99 
  100 
  101 def _gen_hash_crypt(crypt_salt=None, password=None, algorithm=None):
  102     """
  103     Generate /etc/shadow hash using the native crypt module
  104     """
  105     if crypt_salt is None:
  106         # setting crypt_salt to the algorithm makes crypt generate
  107         #  a salt compatible with the specified algorithm.
  108         crypt_salt = methods[algorithm]
  109     else:
  110         if algorithm != "crypt":
  111             # all non-crypt algorithms are specified as part of the salt
  112             crypt_salt = "${}${}".format(methods[algorithm].ident, crypt_salt)
  113 
  114     try:
  115         ret = crypt.crypt(password, crypt_salt)
  116     except OSError:
  117         ret = None
  118     return ret
  119 
  120 
  121 def gen_hash(crypt_salt=None, password=None, algorithm=None):
  122     """
  123     Generate /etc/shadow hash
  124     """
  125     if password is None:
  126         password = secure_password()
  127 
  128     if algorithm is None:
  129         # prefer the most secure natively supported method
  130         algorithm = crypt.methods[0].name.lower() if HAS_CRYPT else known_methods[0]
  131 
  132     if algorithm == "crypt" and crypt_salt and len(crypt_salt) != 2:
  133         log.warning("Hash salt is too long for 'crypt' hash.")
  134 
  135     if HAS_CRYPT and algorithm in methods:
  136         return _gen_hash_crypt(
  137             crypt_salt=crypt_salt, password=password, algorithm=algorithm
  138         )
  139     elif HAS_PASSLIB and algorithm in known_methods:
  140         return _gen_hash_passlib(
  141             crypt_salt=crypt_salt, password=password, algorithm=algorithm
  142         )
  143     else:
  144         raise SaltInvocationError(
  145             "Cannot hash using '{}' hash algorithm. Natively supported "
  146             "algorithms are: {}. If passlib is installed ({}), the supported "
  147             "algorithms are: {}.".format(
  148                 algorithm, list(methods), HAS_PASSLIB, known_methods
  149             )
  150         )