The branch, master has been updated
       via  8b84282008d util:datablob: data_blob_pad checks its alignment 
assumption
       via  929f4d0ca57 bootstrap: we don't need python-crypt-r anymore
       via  552053b6445 pytest: password_hash uses internal _glue.crypt
       via  4af4dd8135e samba-tool user: hashlib.sha1 is always present
       via  405187d2ef4 samba-tool user: use _glue.crypt, not crypt.crypt
       via  5636d30c095 pytest: test that _glue.crypt works
       via  88e3c82d88a pyglue: add crypt() function
       via  5f365e71c1f util: add a crypt strerror helper
       via  c7597380b47 dsdb:password_hash: use talloc_crypt_blob()
       via  1edb12f7959 dsdb:password_hash: move hash_blob allocation up
       via  833455c7f9f util: add a crypt wrapper, derived from 
dsdb:password_hash
      from  5ad1a93107a samba-tool: no traceback for unauthenticated rootdse 
access

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 8b84282008dc372d67ba01c8fe256ef756c3dcfb
Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
Date:   Wed Dec 11 14:31:18 2024 +1300

    util:datablob: data_blob_pad checks its alignment assumption
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15756
    
    Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
    Reviewed-by: Andreas Schneider <a...@samba.org>
    
    Autobuild-User(master): Andreas Schneider <a...@cryptomilk.org>
    Autobuild-Date(master): Fri Dec 20 07:59:51 UTC 2024 on atb-devel-224

commit 929f4d0ca57129d493839169ff5b43ae06c0b051
Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
Date:   Thu Dec 12 10:35:02 2024 +1300

    bootstrap: we don't need python-crypt-r anymore
    
    Recent commits made pyglue wrap the same crypt[_r] that dsdb modules
    use, which removes an extenal dependency.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15756
    
    Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
    Reviewed-by: Andreas Schneider <a...@samba.org>

commit 552053b6445611ecef6ac4c11c55ebf92f03571d
Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
Date:   Thu Dec 12 10:46:16 2024 +1300

    pytest: password_hash uses internal _glue.crypt
    
    This will remove an external dependency.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15756
    
    Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
    Reviewed-by: Andreas Schneider <a...@samba.org>

commit 4af4dd8135e8edbe2a16cfdfc7ded8c145c82e98
Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
Date:   Wed Dec 11 15:56:20 2024 +1300

    samba-tool user: hashlib.sha1 is always present
    
    We maybe thought we were checking that sha1 was in hashlib, but we were
    only checking that hashlib is in the Python library (`hashlib.sha1()`
    would not raise ImportError).
    
    The documentation says hashlib always contains sha1 -- if that
    changes, it is better we know by failing noisily with the import error
    at the top of the file.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15756
    
    Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
    Reviewed-by: Andreas Schneider <a...@samba.org>

commit 405187d2ef4920a9a284649c9c3287f5844d5180
Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
Date:   Wed Dec 11 15:54:48 2024 +1300

    samba-tool user: use _glue.crypt, not crypt.crypt
    
    Because we know we have _glue.crypt, and we know it raises exceptions
    rather than returning None, we can simplify the checks.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15756
    
    Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
    Reviewed-by: Andreas Schneider <a...@samba.org>

commit 5636d30c0959fd4a211ee7b8d1b267dcdbf0b963
Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
Date:   Thu Dec 12 10:44:07 2024 +1300

    pytest: test that _glue.crypt works
    
    The test vectors were generated via Python 3.10 crypt module, which
    directly wraps crypt(3), which in this case is from glibc 2.39-0ubuntu8.3.
    
    We mainly test the sha256 and sha512 vectors, which seems to be all we
    use, and which are said to be widely supported.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15756
    
    Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
    Reviewed-by: Andreas Schneider <a...@samba.org>

commit 88e3c82d88a68cf972f8189e1c3718698b49974a
Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
Date:   Wed Dec 11 14:30:15 2024 +1300

    pyglue: add crypt() function
    
    This wraps talloc_crypt_blob() from lib/util/util_crypt.c which in
    turn wraps the system crypt[_r[n]].
    
    We want this because the Python standard library crypt module is going
    away. That one also wrapped the system crypt or crypt_r, so there
    should be no change.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15756
    
    Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
    Reviewed-by: Andreas Schneider <a...@samba.org>

commit 5f365e71c1fa8cdc533159283a5977164b5d39f2
Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
Date:   Wed Dec 11 14:30:04 2024 +1300

    util: add a crypt strerror helper
    
    This will be used by Python also.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15756
    
    Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
    Reviewed-by: Andreas Schneider <a...@samba.org>

commit c7597380b479208e33a403211cec9b3c7bd3f034
Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
Date:   Thu Dec 12 11:16:22 2024 +1300

    dsdb:password_hash: use talloc_crypt_blob()
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15756
    
    Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
    Reviewed-by: Andreas Schneider <a...@samba.org>

commit 1edb12f79593d0b2aac36d5acdaaae6f495772f6
Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
Date:   Wed Dec 11 14:29:21 2024 +1300

    dsdb:password_hash: move hash_blob allocation up
    
    This will make the next patch simpler.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15756
    
    Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
    Reviewed-by: Andreas Schneider <a...@samba.org>

commit 833455c7f9f71583d567e3a53e854567cd8c3b0b
Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz>
Date:   Thu Dec 5 16:35:51 2024 +1300

    util: add a crypt wrapper, derived from dsdb:password_hash
    
    This is going to be used by the dsdb password_hash module, and exposed
    to Python via pyglue.
    
    We're doing this because Python 3.13 has dropped crypt from the Python
    standard library.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15756
    
    Reviewed-by: Andreas Schneider <a...@samba.org>

-----------------------------------------------------------------------

Summary of changes:
 .gitlab-ci-main.yml                                |   2 +-
 bootstrap/config.py                                |   7 --
 bootstrap/generated-dists/fedora41/bootstrap.sh    |   1 -
 bootstrap/generated-dists/fedora41/packages.yml    |   1 -
 bootstrap/sha1sum.txt                              |   2 +-
 lib/util/data_blob.c                               |   2 +-
 lib/util/util_crypt.c                              | 114 +++++++++++++++++++++
 lib/util/util_crypt.h                              |   7 ++
 lib/util/wscript_build                             |   6 ++
 python/pyglue.c                                    |  41 ++++++++
 python/samba/netcmd/user/readpasswords/common.py   |  37 ++-----
 python/samba/tests/glue.py                         |  65 ++++++++++++
 python/samba/tests/password_hash.py                |   4 +-
 python/wscript                                     |   1 +
 source4/dsdb/samdb/ldb_modules/password_hash.c     |  88 +++-------------
 .../dsdb/samdb/ldb_modules/wscript_build_server    |   2 +-
 16 files changed, 266 insertions(+), 114 deletions(-)
 create mode 100644 lib/util/util_crypt.c
 create mode 100644 lib/util/util_crypt.h


Changeset truncated at 500 lines:

diff --git a/.gitlab-ci-main.yml b/.gitlab-ci-main.yml
index a4bfc48d6ba..6a27d035928 100644
--- a/.gitlab-ci-main.yml
+++ b/.gitlab-ci-main.yml
@@ -47,7 +47,7 @@ variables:
   # Set this to the contents of bootstrap/sha1sum.txt
   # which is generated by bootstrap/template.py --render
   #
-  SAMBA_CI_CONTAINER_TAG: d101907857587b6421907c45676497c336d45ea7
+  SAMBA_CI_CONTAINER_TAG: a234da56a3f4ea96265e6c8a308051be3478ab22
   #
   # We use the ubuntu2204 image as default as
   # it matches what we have on atb-devel-224
diff --git a/bootstrap/config.py b/bootstrap/config.py
index e7c0a0deb51..f65b6b3dab7 100644
--- a/bootstrap/config.py
+++ b/bootstrap/config.py
@@ -159,10 +159,6 @@ PKGS = [
     ('', 'python3-libsemanage'),
     ('', 'python3-policycoreutils'),
 
-    # A copy of the `crypt` module that was removed in Python 3.13
-    # See also https://bugzilla.samba.org/show_bug.cgi?id=15756
-    ('', 'python3-crypt-r'),
-
     # perl
     ('libparse-yapp-perl', 'perl-Parse-Yapp'),
     ('perl-modules', ''),
@@ -538,7 +534,6 @@ RPM_DISTS = {
             'ShellCheck': '',
             'shfmt': '',
             'codespell': '',
-            'python3-crypt-r': '',
         }
     },
     'centos9s': {
@@ -558,7 +553,6 @@ RPM_DISTS = {
             'codespell': '',
             'libcephfs-devel': '',  # not available anymore
             'curl': '',  # Use installed curl-minimal
-            'python3-crypt-r': '',
         }
     },
     'fedora41': {
@@ -587,7 +581,6 @@ RPM_DISTS = {
             'keyutils-libs-devel': 'keyutils-devel',
             'krb5-workstation': 'krb5-client',
             'python3-libsemanage': 'python3-semanage',
-            'python3-crypt-r': '',
             'openldap-devel': 'openldap2-devel',
             'perl-Archive-Tar': 'perl-Archive-Tar-Wrapper',
             'perl-JSON-Parse': 'perl-JSON-XS',
diff --git a/bootstrap/generated-dists/fedora41/bootstrap.sh 
b/bootstrap/generated-dists/fedora41/bootstrap.sh
index 941f7b32185..22d2fa89745 100755
--- a/bootstrap/generated-dists/fedora41/bootstrap.sh
+++ b/bootstrap/generated-dists/fedora41/bootstrap.sh
@@ -92,7 +92,6 @@ dnf install -y \
     procps-ng \
     psmisc \
     python3 \
-    python3-crypt-r \
     python3-cryptography \
     python3-dateutil \
     python3-devel \
diff --git a/bootstrap/generated-dists/fedora41/packages.yml 
b/bootstrap/generated-dists/fedora41/packages.yml
index f00bacbd988..532ac877fdd 100644
--- a/bootstrap/generated-dists/fedora41/packages.yml
+++ b/bootstrap/generated-dists/fedora41/packages.yml
@@ -81,7 +81,6 @@ packages:
   - procps-ng
   - psmisc
   - python3
-  - python3-crypt-r
   - python3-cryptography
   - python3-dateutil
   - python3-devel
diff --git a/bootstrap/sha1sum.txt b/bootstrap/sha1sum.txt
index 401c5fda44b..d011893c3ae 100644
--- a/bootstrap/sha1sum.txt
+++ b/bootstrap/sha1sum.txt
@@ -1 +1 @@
-d101907857587b6421907c45676497c336d45ea7
+a234da56a3f4ea96265e6c8a308051be3478ab22
diff --git a/lib/util/data_blob.c b/lib/util/data_blob.c
index 470aa67cc61..84e814f2ae8 100644
--- a/lib/util/data_blob.c
+++ b/lib/util/data_blob.c
@@ -290,7 +290,7 @@ _PUBLIC_ bool data_blob_pad(TALLOC_CTX *mem_ctx, DATA_BLOB 
*blob,
        size_t old_len = blob->length;
        size_t new_len = (old_len + pad - 1) & ~(pad - 1);
 
-       if (new_len < old_len) {
+       if (new_len < old_len || (pad & (pad - 1)) != 0) {
                return false;
        }
 
diff --git a/lib/util/util_crypt.c b/lib/util/util_crypt.c
new file mode 100644
index 00000000000..09cd47597d1
--- /dev/null
+++ b/lib/util/util_crypt.c
@@ -0,0 +1,114 @@
+#include <replace.h>
+#include "data_blob.h"
+#include <talloc.h>
+#include <crypt.h>
+#include "util_crypt.h"
+
+
+static int crypt_as_best_we_can(const char *phrase,
+                               const char *setting,
+                               const char **hashp)
+{
+       int ret = 0;
+       const char *hash = NULL;
+
+#if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT_RN)
+       struct crypt_data crypt_data = {
+               .initialized = 0        /* working storage used by crypt */
+       };
+#endif
+
+       /*
+        * crypt_r() and crypt() may return a null pointer upon error
+        * depending on how libcrypt was configured, so we prefer
+        * crypt_rn() from libcrypt / libxcrypt which always returns
+        * NULL on error.
+        *
+        * POSIX specifies returning a null pointer and setting
+        * errno.
+        *
+        * RHEL 7 (which does not use libcrypt / libxcrypt) returns a
+        * non-NULL pointer from crypt_r() on success but (always?)
+        * sets errno during internal processing in the NSS crypto
+        * subsystem.
+        *
+        * By preferring crypt_rn we avoid the 'return non-NULL but
+        * set-errno' that we otherwise cannot tell apart from the
+        * RHEL 7 behaviour.
+        */
+       errno = 0;
+
+#ifdef HAVE_CRYPT_RN
+       hash = crypt_rn(phrase, setting,
+                       &crypt_data,
+                       sizeof(crypt_data));
+#elif HAVE_CRYPT_R
+       hash = crypt_r(phrase, setting, &crypt_data);
+#else
+       /*
+        * No crypt_r falling back to crypt, which is NOT thread safe
+        * Thread safety MT-Unsafe race:crypt
+        */
+       hash = crypt(phrase, setting);
+#endif
+       /*
+       * On error, crypt() and crypt_r() may return a null pointer,
+       * or a pointer to an invalid hash beginning with a '*'.
+       */
+       ret = errno;
+       errno = 0;
+       if (hash == NULL || hash[0] == '*') {
+               if (ret == 0) {
+                       /* this is annoying */
+                       ret = ENOTRECOVERABLE;
+               }
+       }
+
+       *hashp = hash;
+       return ret;
+}
+
+
+int talloc_crypt_blob(TALLOC_CTX *mem_ctx,
+                     const char *phrase,
+                     const char *setting,
+                     DATA_BLOB *blob)
+{
+       const char *hash = NULL;
+       int ret = crypt_as_best_we_can(phrase, setting, &hash);
+       if (ret != 0) {
+               blob->data = NULL;
+               blob->length = 0;
+               return ret;
+       }
+       blob->length = strlen(hash);
+       blob->data = talloc_memdup(mem_ctx, hash, blob->length);
+       if (blob->data == NULL) {
+               return ENOMEM;
+       }
+       return 0;
+}
+
+
+char *talloc_crypt_errstring(TALLOC_CTX *mem_ctx, int error)
+{
+       char buf[1024];
+       int err;
+       if (error == ERANGE) {
+               return talloc_strdup(
+                       mem_ctx,
+                       "Password exceeds maximum length allowed for crypt() 
hashing");
+       }
+       if (error == ENOTRECOVERABLE) {
+               /* probably weird RHEL7 crypt, see crypt_as_best_we_can() */
+               goto unknown;
+       }
+
+       err = strerror_r(error, buf, sizeof(buf));
+       if (err != 0) {
+               goto unknown;
+       }
+       return talloc_strndup(mem_ctx, buf, sizeof(buf));
+unknown:
+       return talloc_strdup(mem_ctx, "Unknown error");
+}
diff --git a/lib/util/util_crypt.h b/lib/util/util_crypt.h
new file mode 100644
index 00000000000..ca1a58e922c
--- /dev/null
+++ b/lib/util/util_crypt.h
@@ -0,0 +1,7 @@
+
+int talloc_crypt_blob(TALLOC_CTX *mem_ctx,
+                     const char *phrase,
+                     const char *cmd,
+                     DATA_BLOB *blob);
+
+char *talloc_crypt_errstring(TALLOC_CTX *mem_ctx, int error);
diff --git a/lib/util/wscript_build b/lib/util/wscript_build
index b4fcfeaba07..7de9c0b7b17 100644
--- a/lib/util/wscript_build
+++ b/lib/util/wscript_build
@@ -253,6 +253,12 @@ else:
                       private_library=True,
                       local_include=False)
 
+    bld.SAMBA_LIBRARY('util_crypt',
+                      source='util_crypt.c',
+                      deps='talloc crypt',
+                      private_library=True,
+                      local_include=False)
+
 
     bld.SAMBA_SUBSYSTEM('UNIX_PRIVS',
                         source='unix_privs.c',
diff --git a/python/pyglue.c b/python/pyglue.c
index 5598c0929f3..0e6822b5596 100644
--- a/python/pyglue.c
+++ b/python/pyglue.c
@@ -18,6 +18,7 @@
 */
 
 #include "lib/replace/system/python.h"
+#include "pyerrors.h"
 #include "python/py3compat.h"
 #include "includes.h"
 #include "python/modules.h"
@@ -25,6 +26,7 @@
 #include "param/pyparam.h"
 #include "lib/socket/netif.h"
 #include "lib/util/debug.h"
+#include "lib/util/util_crypt.h"
 #include "librpc/ndr/ndr_private.h"
 #include "lib/cmdline/cmdline.h"
 #include "lib/crypto/gkdi.h"
@@ -529,6 +531,42 @@ static PyObject *py_get_burnt_commandline(PyObject *self, 
PyObject *args)
        return ret;
 }
 
+static PyObject *py_crypt(PyObject *self, PyObject *args)
+{
+       PyObject *py_hash = NULL;
+       char *phrase = NULL;
+       char *setting = NULL;
+       TALLOC_CTX *frame = NULL;
+       int ret;
+       DATA_BLOB hash = {};
+
+       if (!PyArg_ParseTuple(args, "ss", &phrase, &setting)) {
+               TALLOC_FREE(frame);
+               return NULL;
+       }
+       frame = talloc_stackframe();
+       ret = talloc_crypt_blob(frame, phrase, setting, &hash);
+       if (ret != 0) {
+               const char *errstr = talloc_crypt_errstring(frame, ret);
+               if (ret == EINVAL || ret == ERANGE || ret == ENOTRECOVERABLE) {
+                       PyErr_Format(PyExc_ValueError,
+                                    "could not crypt(): %s",
+                                    errstr);
+               } else {
+                       PyErr_Format(PyExc_OSError,
+                                    "could not crypt(): %s",
+                                    errstr);
+               }
+               TALLOC_FREE(frame);
+               return NULL;
+       }
+
+       py_hash = PyUnicode_FromStringAndSize((char *)hash.data, hash.length);
+       TALLOC_FREE(frame);
+       return py_hash;
+}
+
+
 static PyMethodDef py_misc_methods[] = {
        { "generate_random_str", (PyCFunction)py_generate_random_str, 
METH_VARARGS,
                "generate_random_str(len) -> string\n"
@@ -590,6 +628,9 @@ static PyMethodDef py_misc_methods[] = {
                METH_NOARGS, "How many NDR internal tokens is too many for this 
build?" },
        { "get_burnt_commandline", (PyCFunction)py_get_burnt_commandline,
                METH_VARARGS, "Return a redacted commandline to feed to 
setproctitle (None if no redaction required)" },
+       { "crypt", (PyCFunction)py_crypt,
+               METH_VARARGS,
+               "encrypt as phrase, per crypt(3), as determined by setting." },
        { "is_rust_built", (PyCFunction)py_is_rust_built, METH_NOARGS,
                "is Samba built with Rust?" },
        {0}
diff --git a/python/samba/netcmd/user/readpasswords/common.py 
b/python/samba/netcmd/user/readpasswords/common.py
index 1bf29fe3eca..a6cfcb6c5d3 100644
--- a/python/samba/netcmd/user/readpasswords/common.py
+++ b/python/samba/netcmd/user/readpasswords/common.py
@@ -26,6 +26,7 @@ import datetime
 import errno
 import io
 import os
+from hashlib import sha1
 
 import ldb
 from samba import credentials, nttime2float
@@ -37,6 +38,7 @@ from samba.netcmd import Command, CommandError
 from samba.samdb import SamDB
 from samba.nt_time import timedelta_from_nt_time_delta, nt_time_from_datetime
 from samba.gkdi import MAX_CLOCK_SKEW
+from samba._glue import crypt
 
 # python[3]-gpgme is abandoned since ubuntu 1804 and debian 9
 # have to use python[3]-gpg instead
@@ -132,9 +134,7 @@ def get_crypt_value(alg, utf8pw, rounds=0):
     else:
         crypt_salt = "$%s$%s$" % (alg, b64salt)
 
-    crypt_value = crypt.crypt(utf8pw, crypt_salt)
-    if crypt_value is None:
-        raise NotImplementedError("crypt.crypt(%s) returned None" % 
(crypt_salt))
+    crypt_value = crypt(utf8pw, crypt_salt)
     expected_len = len(crypt_salt) + algs[alg]["length"]
     if len(crypt_value) != expected_len:
         raise NotImplementedError("crypt.crypt(%s) returned a value with 
length %d, expected length is %d" % (
@@ -142,35 +142,18 @@ def get_crypt_value(alg, utf8pw, rounds=0):
     return crypt_value
 
 
-try:
-    import hashlib
-    hashlib.sha1()
-    virtual_attributes["virtualSSHA"] = {
-    }
-except ImportError as e:
-    reason = "hashlib.sha1()"
-    reason += " required"
-    disabled_virtual_attributes["virtualSSHA"] = {
-        "reason": reason,
-    }
+
+virtual_attributes["virtualSSHA"] = {}
 
 for (alg, attr) in [("5", "virtualCryptSHA256"), ("6", "virtualCryptSHA512")]:
     try:
-        import crypt
         get_crypt_value(alg, "")
-        virtual_attributes[attr] = {
-        }
-    except ImportError as e:
-        reason = "crypt"
-        reason += " required"
-        disabled_virtual_attributes[attr] = {
-            "reason": reason,
-        }
-    except NotImplementedError as e:
-        reason = "modern '$%s$' salt in crypt(3) required" % (alg)
+    except (ValueError, OSError):
         disabled_virtual_attributes[attr] = {
-            "reason": reason,
+            "reason": f"modern '${alg}$' salt in crypt(3) required"
         }
+        continue
+    virtual_attributes[attr] = {}
 
 # Add the wDigest virtual attributes, virtualWDigest01 to virtualWDigest29
 for x in range(1, 30):
@@ -749,7 +732,7 @@ class GetPasswordCommand(Command):
                 if u8 is None:
                     continue
                 salt = os.urandom(4)
-                h = hashlib.sha1()
+                h = sha1()
                 h.update(u8)
                 h.update(salt)
                 bv = h.digest() + salt
diff --git a/python/samba/tests/glue.py b/python/samba/tests/glue.py
index ac504b3f366..824f5ca0c81 100644
--- a/python/samba/tests/glue.py
+++ b/python/samba/tests/glue.py
@@ -88,3 +88,68 @@ class GlueTests(samba.tests.TestCase):
         self.assertEqual(_glue.strstr_m(string, '_'), '_string_num__one')
         self.assertEqual(_glue.strstr_m(string, '__'), '__one')
         self.assertEqual(_glue.strstr_m(string, 'ring'), 'ring_num__one')
+
+    def test_crypt(self):
+        # We hopefully only use schemes 5 and 6 (sha256 and sha512),
+        # which are OK and also quite widely supported according to
+        # https://en.wikipedia.org/wiki/Crypt_(C)
+        for phrase, setting, expected in [
+                ("a", "$5$aaaaaa",
+                 "$5$aaaaaa$F4lxguL7mZR7TGlvukPTJIxoRhVmHMZs8ZdH8oDP0.6"),
+                # with scheme 5, 5000 rounds is default, so hash is the same 
as above
+                ('a', '$5$rounds=5000$aaaaaa',
+                 
'$5$rounds=5000$aaaaaa$F4lxguL7mZR7TGlvukPTJIxoRhVmHMZs8ZdH8oDP0.6'),
+                ('a',
+                 '$5$rounds=4999$aaaaaa',
+                 
'$5$rounds=4999$aaaaaa$FiP70gtxOJUFLokUJvET06E7jbL6aNmF6Wtv2ddzjY8'),
+                ('a', '$5$aaaaab',
+                 '$5$aaaaab$e9qR2F833/JyuMu.nkQc9kn184vBWLo0ODqnCe./mj0'),
+
+                ('', '$5$aaaaaa', 
'$5$aaaaaa$5B4WTdWp5n/v/aNUw2N8RsEitqvlZJEaAKhH/pOkGg4'),
+
+                ("a", "$6$aaaaaa",
+                 
"$6$aaaaaa$KHs/Ez7X/I5/K.V8FR7kEsx9rOvjXnEDUmGC.dLBWP87XWy.oUEAM7QYcZQRVhiDwGepOF2pKrCVETYLyASh60"),
+
+                ('', '$5$', '$5$$3c2QQ0KjIU1OLtB29cl8Fplc2WN7X89bnoEjaR7tWu.'),
+
+                # scheme 1 (md5) should be supported if not used
+                ('a', '$1$aaaaaa',
+                 '$1$aaaaaa$MUMWPbGfzrHFCNm7ZHg31.'),
+
+                ('', '$6$',
+                 
'$6$$/chiBau24cE26QQVW3IfIe68Xu5.JQ4E8Ie7lcRLwqxO5cxGuBhqF2HmTL.zWJ9zjChg3yJYFXeGBQ2y3Ba1d1'),
+                (' ',
+                 '$6$6',
+                 
'$6$6$asLnbxf0obyuv3ybNvDE9ZcdwGFkDhLe7uW.wzdOdKCm4/M3vGFKq4Ttk1tBQrOn4wALZ3tj1L8IarIu5i8hR/'),
+
+                # original DES scheme, 12 bits of salt
+                ("a", "lalala", "laKGbFzgh./R2"),
+                ("a", "lalalaLALALAla", "laKGbFzgh./R2"),
+                ("a", "arrgh", "ar7VUiUvDhX2c"),
+                ("a", "arrggghhh", "ar7VUiUvDhX2c"),
+                ]:
+            hash = _glue.crypt(phrase, setting)
+            self.assertEqual(hash, expected)
+
+    def test_crypt_bad(self):
+        # We can't be too strident in our assertions, because every
+        # system allows a different set of algorithms, and some have
+        # different ideas of how to parse.
+        for phrase, setting, exception in [
+                ("a", "$5", ValueError),
+                ("a", "$0$", ValueError),
+                ("a", None, TypeError),
+                (None, "", TypeError),
+                ('a', '$66$', ValueError),
+                ('a', '$$', ValueError),
+                ('a', '*0', ValueError),
+                ('a', '*', ValueError),
+                ('a', '++', ValueError),
+                # this next one is too long, except on Rocky Linux 8.
+                #('a' * 10000, '$5$5', ValueError),
+                # this is invalid, except on Debian 11.
+                # (' ', '$6$ ', ValueError),
+                ]:
+            with self.assertRaises(exception,
+                                   msg=f"crypt({phrase!r}, {setting!r}) didn't 
fail"):
+                _glue.crypt(phrase, setting)
diff --git a/python/samba/tests/password_hash.py 
b/python/samba/tests/password_hash.py
index 1b7af7de7b8..39ef13fd7b2 100644
--- a/python/samba/tests/password_hash.py
+++ b/python/samba/tests/password_hash.py
@@ -30,11 +30,11 @@ from samba.dcerpc.samr import 
DOMAIN_PASSWORD_STORE_CLEARTEXT
 from samba.dsdb import UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED
 from samba.tests import delete_force
 from samba.tests.password_test import PasswordCommon
+from samba._glue import crypt
 import ldb
 import samba
 import binascii
 from hashlib import md5
-import crypt
 
 
 USER_NAME = "PasswordHashTestUser"
@@ -321,7 +321,7 @@ class PassWordHashTests(TestCase):
                 cmd = "$%s$rounds=%d$%s" % (alg, rounds, data[3])
 
             # Calculate the expected hash value
-            expected = crypt.crypt(USER_PASS, cmd)
+            expected = crypt(USER_PASS, cmd)
             self.assertEqual(expected, up.hashes[i].value.decode('utf8'))
             i += 1
 


-- 
Samba Shared Repository

Reply via email to