Mini Shell
"""
Manage the password database on BSD systems
.. important::
If you feel that Salt should be using this module to manage passwords on a
minion, and it is using a different module (or gives an error similar to
*'shadow.info' is not available*), see :ref:`here
<module-provider-override>`.
"""
import salt.utils.files
import salt.utils.stringutils
from salt.exceptions import CommandExecutionError, SaltInvocationError
try:
import pwd
except ImportError:
pass
try:
import salt.utils.pycrypto
HAS_CRYPT = True
except ImportError:
HAS_CRYPT = False
# Define the module's virtual name
__virtualname__ = "shadow"
def __virtual__():
if "BSD" in __grains__.get("os", ""):
return __virtualname__
return (
False,
"The bsd_shadow execution module cannot be loaded: "
"only available on BSD family systems.",
)
def default_hash():
"""
Returns the default hash used for unset passwords
CLI Example:
.. code-block:: bash
salt '*' shadow.default_hash
"""
return "*" if __grains__["os"].lower() == "freebsd" else "*************"
def gen_password(password, crypt_salt=None, algorithm="sha512"):
"""
Generate hashed password
.. note::
When called this function is called directly via remote-execution,
the password argument may be displayed in the system's process list.
This may be a security risk on certain systems.
password
Plaintext password to be hashed.
crypt_salt
Crpytographic salt. If not given, a random 8-character salt will be
generated.
algorithm
The following hash algorithms are supported:
* md5
* blowfish (not in mainline glibc, only available in distros that add it)
* sha256
* sha512 (default)
CLI Example:
.. code-block:: bash
salt '*' shadow.gen_password 'I_am_password'
salt '*' shadow.gen_password 'I_am_password' crypt_salt='I_am_salt' algorithm=sha256
"""
if not HAS_CRYPT:
raise CommandExecutionError(
"gen_password is not available on this operating system "
'because the "crypt" python module is not available.'
)
return salt.utils.pycrypto.gen_hash(crypt_salt, password, algorithm)
def info(name):
"""
Return information for the specified user
CLI Example:
.. code-block:: bash
salt '*' shadow.info someuser
"""
try:
data = pwd.getpwnam(name)
ret = {"name": data.pw_name, "passwd": data.pw_passwd}
except KeyError:
return {"name": "", "passwd": ""}
if not isinstance(name, str):
name = str(name)
if ":" in name:
raise SaltInvocationError(f"Invalid username '{name}'")
if __salt__["cmd.has_exec"]("pw"):
change, expire = __salt__["cmd.run_stdout"](
["pw", "user", "show", name], python_shell=False
).split(":")[5:7]
elif __grains__["kernel"] in ("NetBSD", "OpenBSD"):
try:
with salt.utils.files.fopen("/etc/master.passwd", "r") as fp_:
for line in fp_:
line = salt.utils.stringutils.to_unicode(line)
if line.startswith(f"{name}:"):
key = line.split(":")
change, expire = key[5:7]
ret["passwd"] = str(key[1])
break
except OSError:
change = expire = None
else:
change = expire = None
try:
ret["change"] = int(change)
except ValueError:
pass
try:
ret["expire"] = int(expire)
except ValueError:
pass
return ret
def set_change(name, change):
"""
Sets the time at which the password expires (in seconds since the UNIX
epoch). See ``man 8 usermod`` on NetBSD and OpenBSD or ``man 8 pw`` on
FreeBSD.
A value of ``0`` sets the password to never expire.
CLI Example:
.. code-block:: bash
salt '*' shadow.set_change username 1419980400
"""
pre_info = info(name)
if change == pre_info["change"]:
return True
if __grains__["kernel"] == "FreeBSD":
cmd = ["pw", "user", "mod", name, "-f", change]
else:
cmd = ["usermod", "-f", change, name]
__salt__["cmd.run"](cmd, python_shell=False)
post_info = info(name)
if post_info["change"] != pre_info["change"]:
return post_info["change"] == change
def set_expire(name, expire):
"""
Sets the time at which the account expires (in seconds since the UNIX
epoch). See ``man 8 usermod`` on NetBSD and OpenBSD or ``man 8 pw`` on
FreeBSD.
A value of ``0`` sets the account to never expire.
CLI Example:
.. code-block:: bash
salt '*' shadow.set_expire username 1419980400
"""
pre_info = info(name)
if expire == pre_info["expire"]:
return True
if __grains__["kernel"] == "FreeBSD":
cmd = ["pw", "user", "mod", name, "-e", expire]
else:
cmd = ["usermod", "-e", expire, name]
__salt__["cmd.run"](cmd, python_shell=False)
post_info = info(name)
if post_info["expire"] != pre_info["expire"]:
return post_info["expire"] == expire
def del_password(name):
"""
.. versionadded:: 2015.8.2
Delete the password from name user
CLI Example:
.. code-block:: bash
salt '*' shadow.del_password username
"""
cmd = f"pw user mod {name} -w none"
__salt__["cmd.run"](cmd, python_shell=False, output_loglevel="quiet")
uinfo = info(name)
return not uinfo["passwd"]
def set_password(name, password):
"""
Set the password for a named user. The password must be a properly defined
hash. A password hash can be generated with :py:func:`gen_password`.
It is important to make sure that a supported cipher is used.
CLI Example:
.. code-block:: bash
salt '*' shadow.set_password someuser '$1$UYCIxa628.9qXjpQCjM4a..'
"""
if __grains__.get("os", "") == "FreeBSD":
cmd = ["pw", "user", "mod", name, "-H", "0"]
stdin = password
else:
cmd = ["usermod", "-p", password, name]
stdin = None
__salt__["cmd.run"](cmd, stdin=stdin, output_loglevel="quiet", python_shell=False)
return info(name)["passwd"] == password
Zerion Mini Shell 1.0