* defend against bad or missing crypt
fixes #74279
(cherry picked from commit 4494ef3a9d0b0816e228a2b0cf8ebce9b732253a)
... | ... |
@@ -4,7 +4,6 @@ |
4 | 4 |
from __future__ import (absolute_import, division, print_function) |
5 | 5 |
__metaclass__ = type |
6 | 6 |
|
7 |
-import crypt |
|
8 | 7 |
import multiprocessing |
9 | 8 |
import random |
10 | 9 |
import string |
... | ... |
@@ -18,15 +17,23 @@ from ansible.module_utils.six import text_type |
18 | 18 |
from ansible.module_utils._text import to_text, to_bytes |
19 | 19 |
from ansible.utils.display import Display |
20 | 20 |
|
21 |
-PASSLIB_AVAILABLE = False |
|
21 |
+PASSLIB_E = CRYPT_E = None |
|
22 |
+HAS_CRYPT = PASSLIB_AVAILABLE = False |
|
22 | 23 |
try: |
23 | 24 |
import passlib |
24 | 25 |
import passlib.hash |
25 | 26 |
from passlib.utils.handlers import HasRawSalt |
26 | 27 |
|
27 | 28 |
PASSLIB_AVAILABLE = True |
28 |
-except Exception: |
|
29 |
- pass |
|
29 |
+except Exception as e: |
|
30 |
+ PASSLIB_E = e |
|
31 |
+ |
|
32 |
+try: |
|
33 |
+ import crypt |
|
34 |
+ HAS_CRYPT = True |
|
35 |
+except Exception as e: |
|
36 |
+ CRYPT_E = e |
|
37 |
+ |
|
30 | 38 |
|
31 | 39 |
display = Display() |
32 | 40 |
|
... | ... |
@@ -77,6 +84,9 @@ class CryptHash(BaseHash): |
77 | 77 |
def __init__(self, algorithm): |
78 | 78 |
super(CryptHash, self).__init__(algorithm) |
79 | 79 |
|
80 |
+ if not HAS_CRYPT: |
|
81 |
+ raise AnsibleError("crypt.crypt cannot be used as the 'crypt' python library is not installed or is unusable.", orig_exc=CRYPT_E) |
|
82 |
+ |
|
80 | 83 |
if sys.platform.startswith('darwin'): |
81 | 84 |
raise AnsibleError("crypt.crypt not supported on Mac OS X/Darwin, install passlib python module") |
82 | 85 |
|
... | ... |
@@ -122,7 +132,7 @@ class PasslibHash(BaseHash): |
122 | 122 |
super(PasslibHash, self).__init__(algorithm) |
123 | 123 |
|
124 | 124 |
if not PASSLIB_AVAILABLE: |
125 |
- raise AnsibleError("passlib must be installed to hash with '%s'" % algorithm) |
|
125 |
+ raise AnsibleError("passlib must be installed and usable to hash with '%s'" % algorithm, orig_exc=PASSLIB_E) |
|
126 | 126 |
|
127 | 127 |
try: |
128 | 128 |
self.crypt_algo = getattr(passlib.hash, algorithm) |
... | ... |
@@ -189,8 +199,10 @@ class PasslibHash(BaseHash): |
189 | 189 |
def passlib_or_crypt(secret, algorithm, salt=None, salt_size=None, rounds=None): |
190 | 190 |
if PASSLIB_AVAILABLE: |
191 | 191 |
return PasslibHash(algorithm).hash(secret, salt=salt, salt_size=salt_size, rounds=rounds) |
192 |
- else: |
|
192 |
+ elif HAS_CRYPT: |
|
193 | 193 |
return CryptHash(algorithm).hash(secret, salt=salt, salt_size=salt_size, rounds=rounds) |
194 |
+ else: |
|
195 |
+ raise AnsibleError("Unable to encrypt nor hash, either crypt or passlib must be installed.", orig_exc=CRYPT_E) |
|
194 | 196 |
|
195 | 197 |
|
196 | 198 |
def do_encrypt(result, encrypt, salt_size=None, salt=None): |
... | ... |
@@ -48,7 +48,7 @@ def assert_hash(expected, secret, algorithm, **settings): |
48 | 48 |
assert encrypt.passlib_or_crypt(secret, algorithm, **settings) == expected |
49 | 49 |
with pytest.raises(AnsibleError) as excinfo: |
50 | 50 |
encrypt.PasslibHash(algorithm).hash(secret, **settings) |
51 |
- assert excinfo.value.args[0] == "passlib must be installed to hash with '%s'" % algorithm |
|
51 |
+ assert excinfo.value.args[0] == "passlib must be installed and usable to hash with '%s'" % algorithm |
|
52 | 52 |
|
53 | 53 |
|
54 | 54 |
@pytest.mark.skipif(sys.platform.startswith('darwin'), reason='macOS requires passlib') |