[SCM] Samba Shared Repository - branch master updated

Andrew Bartlett abartlet at samba.org
Thu May 25 04:29:02 UTC 2017


The branch, master has been updated
       via  fa6753d libnet join: Fix error handling on provision_store_self_join failure
       via  7796364 source4/provision: fix talloc_steal on unallocated memory
       via  468dc02 tests net_join: use private secrets database.
       via  9444bbf source4 rpc: binding.c enable DCERPC_SCHANNEL_AUTO for schannel connections
       via  610919e auth pycredentials: incorrect PyArg_ParseTupleAndKeywords call
       via  ee0eb1d auth pycredentials: correct docstring of get_ntlm_response method
       via  68ccebf auth_log: Add test that execises the SamLogon python bindings
       via  6419909 tests password_hash: Add ldap based tests for WDigest
       via  83fbd80 pynet: Add a hook to decrypt one attribute
       via  f5cd832 tests password_hash: update array indexes for readabliity
       via  b14bb68 samba-tool add support for userPassword
       via  8a5308b samba-tool tests: add tests for userPassword
       via  4b49e18 password_hash: generate and store Primary:userPassword
       via  de5299d tests password_hash: add tests for Primary:userPassword
       via  79f027a docs: configuration options for extra password hashes
       via  adae071 tests password_hash: fix white space issues
       via  601dbca tests password_hash: remove unused import
       via  826e50a idl drsblobs: add the blobs required for Primary:userPassword
       via  d4bc91a samba-tool user: add rounds option to virtualCryptSHAxxx
       via  d512536 samba-tool tests: Tests for virtualCryptSHAxxx rounds
       via  3bcd384 samba-tool user: Support for virtualWDigest attributes
       via  81312ba samba-tool user: Tests for virtualWDigest attributes
      from  f47d331 ctdb-daemon: Add AllowMixedVersions tunable

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


- Log -----------------------------------------------------------------
commit fa6753d6c2f83392e9517bd3cee7d65561fb7c0c
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Tue May 23 14:11:35 2017 +1200

    libnet join: Fix error handling on provision_store_self_join failure
    
    This avoids leaving the error string NULL.
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    
    Autobuild-User(master): Andrew Bartlett <abartlet at samba.org>
    Autobuild-Date(master): Thu May 25 06:28:02 CEST 2017 on sn-devel-144

commit 7796364d623011343c3f19c3167a41ab569eaea0
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Tue May 23 14:13:14 2017 +1200

    source4/provision: fix talloc_steal on unallocated memory
    
    The caller will steal *error_string on failure, if it
    is not NULL.
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 468dc02e84fedbfae2b297f716cb60dec2981ed5
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Tue May 23 13:03:03 2017 +1200

    tests net_join: use private secrets database.
    
    Tests were leaving entries in the secrets database that caused
    subsequent test cases to fail.
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 9444bbfe1829e25f772e05c1c3f3c5aa26a16105
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Thu Apr 27 07:12:34 2017 +1200

    source4 rpc: binding.c enable DCERPC_SCHANNEL_AUTO for schannel connections
    
    Enable the DCERPC_SCHANNEL_AUTO option in dceprc bindings. If not enabled
    calls to netlogon.netlogon from python fail with NT_STATUS_DOWNGRADE_DETECTED
    if schannel bindings are specified.
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Pair-programmed-with: Garming Sam <garming at catalyst.net.nz>

commit 610919e5e665dcb23241055e8ccb11f3d3f2ae2c
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Fri Apr 28 13:14:16 2017 +1200

    auth pycredentials: incorrect PyArg_ParseTupleAndKeywords call
    
    The challenge parameter was being treated as a string rather than as a
    data blob.  This was causing intermittent seg faults. Removed the
    server_timestamp parameter as it's not currently used.
    
    Unable to produce a test case to reliably replicate the failure.
    However auth_log_samlogon does flap
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit ee0eb1daa349671386bae0121be6a4b8be06815d
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Fri Apr 28 13:13:28 2017 +1200

    auth pycredentials: correct docstring of get_ntlm_response method
    
    Fix copy paste error was incorrectly named "get_ntlm_username_domain"
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 68ccebfa59e2fc8b717c93224594f49af556ec6e
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Fri Apr 28 10:16:39 2017 +1200

    auth_log: Add test that execises the SamLogon python bindings
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 64199090940cad9f3b4c3f5781426e61418dde9b
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Mon May 22 09:49:17 2017 +1200

    tests password_hash: Add ldap based tests for WDigest
    
    Add tests of the WDigest values using ldap.  This allows the tests to be
    run against Windows, to validate the calculated values.
    
    Tests validated against Windows Server 2012 R2
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 83fbd80b3f11f94dc00950c270daf9288148a658
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Wed May 17 17:05:13 2017 +1200

    pynet: Add a hook to decrypt one attribute
    
    This will help with testing GetNCChanges and supplementalCredentials against Windows in Python
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>

commit f5cd83247fa2384744873637fd2d32efe4f65ec3
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Thu May 18 14:38:37 2017 +1200

    tests password_hash: update array indexes for readabliity
    
    Use an n-1 pattern in the indexes to the digest array to simplify checking
    against the documentation and samba-tool user tests.
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit b14bb68417e34db17a9b7ba4feac7b32ef2cd39e
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Mon May 15 12:19:22 2017 +1200

    samba-tool add support for userPassword
    
    Changes to virtualCryptSHA256 and virtualCryptSHA512 attributes.
    The values are now calculated as follows:
      1) If a value exists in 'Primary:userPassword' with
         the specified number of rounds it is returned.
      2) If 'Primary:CLEARTEXT, or 'Primary:SambaGPG' with
         '--decrypt-samba-gpg'. Calculate a hash with the specified number of rounds
      3) Return the first {CRYPT} value in 'Primary:userPassword' with a
         matching algorithm
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 8a5308bea054686a25aba5b933bc02ad1f2e4587
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Mon May 15 12:20:58 2017 +1200

    samba-tool tests: add tests for userPassword
    
    Tests to ensure that precomputed SHA256 and SHA512 hashes in
    'supplementalCredentials Primary:userPassword' are used correctly in the
    calculation of virtualCryptSHA256 and virtualCryptSHA512
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 4b49e18c143b67bf8f3c1377c0f61b75f507f7fc
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Tue Apr 4 16:05:08 2017 +1200

    password_hash: generate and store Primary:userPassword
    
    Generate sha256 and sha512 password hashes and store them in
    supplementalCredentials
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit de5299d1554130aef9554d2696742ceaab66daeb
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Wed Apr 12 09:12:56 2017 +1200

    tests password_hash: add tests for Primary:userPassword
    
        Add tests to verify the generation and storage of sha256 and sha512
        password hashes in suplementalCredentials Primary:userPassword
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 79f027a6104d9022437ced8570d31857e74346ea
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Tue May 9 14:38:06 2017 +1200

    docs: configuration options for extra password hashes
    
    Add the configuration options for the generation and storage of crypt()
    based sha256 and sha512 password hashes in supplementalCredentials
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit adae071daad3f3508c389fe2933e5c7f98a45be6
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Wed Apr 12 09:09:27 2017 +1200

    tests password_hash: fix white space issues
    
    Clean up white space issues in password_hash.py
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 601dbca8f9617c5632601f8a8fb26ba4426f0fca
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Wed Apr 12 09:08:24 2017 +1200

    tests password_hash: remove unused import
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 826e50a5f62146bae5827736eecdea63726ab996
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Tue Apr 4 16:00:20 2017 +1200

    idl drsblobs: add the blobs required for Primary:userPassword
    
    Add the blobs required to allow the storing of an sha256 or sha512 hash of
    the password in supplemental credentials
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit d4bc91a964725c0a0fb8f8c3aa48fcb13f4a998e
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Tue May 9 11:20:15 2017 +1200

    samba-tool user: add rounds option to virtualCryptSHAxxx
    
    Allow the number of rounds to be specified when calculating the
    virtualCryptSHA256 and virtualCryptSHA512 attributes.
    
    i.e. --attributes="virtualCryptSHA256;rounds=3000" will calculate the
    hash using 3,000 rounds.
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit d51253609de61b8c7c75d0de8febe9c2c0afdae7
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Tue May 9 11:15:19 2017 +1200

    samba-tool tests: Tests for virtualCryptSHAxxx rounds
    
    Add tests to for the new rounds option for the virtualCryptSHA256 and
    virtualCryptSHA512 attributes.
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 3bcd384dcf0ad5ed4dd1e94e30507e75d1006e73
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Mon May 8 10:00:58 2017 +1200

    samba-tool user: Support for virtualWDigest attributes
    
    Add new virtualWDigest attributes, these return the hashes stored in
    supplementalCredentials Primary:WDigest, in a form suitable for
    htdigest authentication
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 81312ba4e2f3d34e18513e4af966fedd86077085
Author: Gary Lockyer <gary at catalyst.net.nz>
Date:   Mon May 8 10:00:09 2017 +1200

    samba-tool user: Tests for virtualWDigest attributes
    
    Add tests for the new virtualWDigest attributes, these return the hashes
    stored in supplementalCredentials Primary:WDigest in a form suitable for
    use with htdigest authentication.
    
    Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

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

Summary of changes:
 auth/credentials/pycredentials.c                   |  31 +-
 .../security/passwordhashuserpasswordschemes.xml   |  67 +++
 librpc/idl/drsblobs.idl                            |  44 ++
 librpc/rpc/binding.c                               |   2 +-
 python/samba/netcmd/user.py                        | 327 ++++++++++++-
 python/samba/provision/__init__.py                 |   5 +-
 python/samba/tests/auth_log_samlogon.py            | 181 +++++++
 .../tests/{net_join_no_spnego.py => net_join.py}   |  26 +-
 python/samba/tests/net_join_no_spnego.py           |  41 +-
 python/samba/tests/password_hash.py                | 169 ++++---
 python/samba/tests/password_hash_fl2003.py         |  98 +++-
 python/samba/tests/password_hash_fl2008.py         |  98 ++++
 python/samba/tests/password_hash_gpgme.py          | 106 ++++
 python/samba/tests/password_hash_ldap.py           | 132 +++++
 .../samba/tests/samba_tool/user_virtualCryptSHA.py | 540 +++++++++++++++++++++
 python/samba/tests/samba_tool/user_wdigest.py      | 456 +++++++++++++++++
 source4/dsdb/pydsdb.c                              |   1 +
 source4/dsdb/samdb/ldb_modules/password_hash.c     | 269 +++++++++-
 source4/libnet/libnet_join.c                       |   9 +-
 source4/libnet/py_net.c                            |  74 +++
 source4/param/provision.c                          |   3 +
 source4/selftest/tests.py                          |  24 +-
 22 files changed, 2587 insertions(+), 116 deletions(-)
 create mode 100644 docs-xml/smbdotconf/security/passwordhashuserpasswordschemes.xml
 create mode 100644 python/samba/tests/auth_log_samlogon.py
 copy python/samba/tests/{net_join_no_spnego.py => net_join.py} (68%)
 create mode 100644 python/samba/tests/password_hash_ldap.py
 create mode 100644 python/samba/tests/samba_tool/user_virtualCryptSHA.py
 create mode 100644 python/samba/tests/samba_tool/user_wdigest.py


Changeset truncated at 500 lines:

diff --git a/auth/credentials/pycredentials.c b/auth/credentials/pycredentials.c
index 283a73f..fee9556 100644
--- a/auth/credentials/pycredentials.c
+++ b/auth/credentials/pycredentials.c
@@ -90,16 +90,19 @@ static PyObject *py_creds_get_ntlm_response(PyObject *self, PyObject *args, PyOb
 	DATA_BLOB lm_session_key = data_blob_null;
 	DATA_BLOB nt_session_key = data_blob_null;
 	const char *kwnames[] = { "flags", "challenge",
-				  "target_info", "server_timestamp",
+				  "target_info",
 				  NULL };
 
 	tv_now = timeval_current();
 	server_timestamp = timeval_to_nttime(&tv_now);
 
-	if (!PyArg_ParseTupleAndKeywords(args, kwargs,
-					 "i" PYARG_BYTES_LEN "|" PYARG_BYTES_LEN "K",
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "is#|s#",
 					 discard_const_p(char *, kwnames),
-					 &flags, &challenge, &target_info.data, &target_info.length)) {
+					 &flags,
+					 &challenge.data,
+					 &challenge.length,
+					 &target_info.data,
+					 &target_info.length)) {
 		return NULL;
 	}
 
@@ -581,6 +584,20 @@ static PyObject *py_creds_get_gensec_features(PyObject *self, PyObject *args)
 	return PyInt_FromLong(gensec_features);
 }
 
+static PyObject *py_creds_set_secure_channel_type(PyObject *self, PyObject *args)
+{
+	unsigned int channel_type;
+
+	if (!PyArg_ParseTuple(args, "I", &channel_type))
+		return NULL;
+
+	cli_credentials_set_secure_channel_type(
+		PyCredentials_AsCliCredentials(self),
+		channel_type);
+
+	Py_RETURN_NONE;
+}
+
 
 static PyMethodDef py_creds_methods[] = {
 	{ "get_username", py_creds_get_username, METH_NOARGS,
@@ -600,8 +617,8 @@ static PyMethodDef py_creds_methods[] = {
 		"S.get_ntlm_username_domain() -> (domain, username)\n"
 		"Obtain NTLM username and domain, split up either as (DOMAIN, user) or (\"\", \"user at realm\")." },
 	{ "get_ntlm_response", (PyCFunction)py_creds_get_ntlm_response, METH_VARARGS | METH_KEYWORDS,
-		"S.get_ntlm_username_domain"
-	        "(flags, challenge, target_info[, server_timestamp]) -> "
+		"S.get_ntlm_response"
+	        "(flags, challenge[, target_info]) -> "
 	        "(flags, lm_response, nt_response, lm_session_key, nt_session_key)\n"
 		"Obtain LM or NTLM response." },
 	{ "set_password", py_creds_set_password, METH_VARARGS,
@@ -683,6 +700,8 @@ static PyMethodDef py_creds_methods[] = {
 	{ "set_forced_sasl_mech", py_creds_set_forced_sasl_mech, METH_VARARGS,
 		"S.set_forced_sasl_mech(name) -> None\n"
 		"Set forced SASL mechanism." },
+	{ "set_secure_channel_type", py_creds_set_secure_channel_type,
+	  METH_VARARGS, NULL },
 	{ NULL }
 };
 
diff --git a/docs-xml/smbdotconf/security/passwordhashuserpasswordschemes.xml b/docs-xml/smbdotconf/security/passwordhashuserpasswordschemes.xml
new file mode 100644
index 0000000..18a43f9
--- /dev/null
+++ b/docs-xml/smbdotconf/security/passwordhashuserpasswordschemes.xml
@@ -0,0 +1,67 @@
+<samba:parameter name="password hash userPassword schemes"
+                 context="G"
+                 type="cmdlist"
+                 xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+
+<para>This parameter determines whether or not
+<citerefentry><refentrytitle>samba</refentrytitle>
+<manvolnum>8</manvolnum></citerefentry> acting as an Active
+Directory Domain Controller will attempt to store additional
+passwords hash types for the user</para>
+
+<para>The values are stored as 'Primary:userPassword' in the
+<command moreinfo="none">supplementalCredentials</command>
+attribute.  The value of this option is a hash type.</para>
+
+<para>The currently supported hash types are:</para>
+<itemizedlist>
+   <listitem>
+       <para><constant>CryptSHA256</constant></para>
+   </listitem>
+   <listitem>
+       <para><constant>CryptSHA512</constant></para>
+   </listitem>
+</itemizedlist>
+
+<para>Multiple instances of a hash type may be computed and stored.
+The password hashes are calculated using the
+<citerefentry><refentrytitle>crypt</refentrytitle>
+<manvolnum>3</manvolnum></citerefentry> call.
+The number of rounds used to compute the hash can be specified by adding
+':rounds=xxxx' to the hash type, i.e. CryptSHA512:rounds=4500 would calculate
+an SHA512 hash using 4500 rounds.  If not specified the Operating System
+defaults for
+<citerefentry><refentrytitle>crypt</refentrytitle>
+<manvolnum>3</manvolnum></citerefentry> are used.
+</para>
+
+<para>As password changes can occur on any domain controller,
+you should configure this on each of them. Note that this feature is
+currently available only on Samba domain controllers.</para>
+
+<para>Currently the NT Hash of the password is recorded when these hashes
+are calculated and stored.  When retrieving the hashes the current value of the
+NT Hash is checked against the stored NT Hash. This detects password changes
+that have not updated the password hashes.  In this case
+<command moreinfo="none">samba-tool user</command> will ignore the stored
+hash values.
+</para>
+
+<para>Being able to obtain the hashed password helps, when
+they need to be imported into other authentication systems
+later (see <command moreinfo="none">samba-tool user
+getpassword</command>) or you want to keep the passwords in
+sync with another system, e.g. an OpenLDAP server (see
+<command moreinfo="none">samba-tool user
+syncpasswords</command>).</para>
+
+<related>unix password sync</related>
+
+</description>
+
+<value type="default"></value>
+<value type="example">CryptSHA256</value>
+<value type="example">CryptSHA256 CryptSHA512</value>
+<value type="example">CryptSHA256:rounds=5000 CryptSHA512:rounds=7000</value>
+</samba:parameter>
diff --git a/librpc/idl/drsblobs.idl b/librpc/idl/drsblobs.idl
index 44f5fda..9fca2cb 100644
--- a/librpc/idl/drsblobs.idl
+++ b/librpc/idl/drsblobs.idl
@@ -323,6 +323,16 @@ interface drsblobs {
 		 * 'Primary:CLEARTEXT':
 		 *    data contains the cleartext password
 		 *    as UTF16 string encoded as HEX string
+		 *
+		 * 'Primary:userPassword':
+		 *    ...
+		 *
+		 * 'Primary:SambaGPG':
+		 *    ...
+		 *
+		 * 'Primary:NTLM-Strong-NTOWF':
+		 *    ... Not yet implemented.
+		 *
 		 */
 		[charset(DOS)] uint8 data[data_len];
 	} supplementalCredentialsPackage;
@@ -460,6 +470,40 @@ interface drsblobs {
 		[in] package_PrimarySambaGPGBlob blob
 		);
 
+	/*
+	 * Password hashes stored in a scheme compatible with
+	 * OpenLDAP's userPassword attribute. The Package is named
+	 * Primary:userPassword each calculated hash,
+	 * which is typically caclulated via crypt(), the scheme is stored.
+	 * The scheme name and the {scheme} format is re-used from OpenLDAP's
+	 * use for userPassword to aid interopability when exported.
+	 *
+	 * The currently supported scheme so far is {CRYPT}, which may
+	 * be specified multiple times if both CryptSHA256 ($5$) and
+	 * CryptSHA512 ($6$) are in use.
+	 *
+	 * current_nt_hash is either the unicodePwd or the
+	 * NTLM-Strong-NTOWF, to allow us to prove this password is
+	 * a valid element.
+	 */
+	typedef struct {
+		[value(2*strlen_m(scheme))] uint16 scheme_len;
+		[charset(UTF16)] uint8 scheme[scheme_len];
+		[value((value?value->length:0))] uint32 value_len;
+		[relative,subcontext(0),subcontext_size(value_len),
+			flag(NDR_REMAINING)] DATA_BLOB *value;
+	} package_PrimaryUserPasswordValue;
+
+	typedef [public] struct {
+		samr_Password current_nt_hash;
+		uint16 num_hashes;
+		package_PrimaryUserPasswordValue hashes[num_hashes];
+	} package_PrimaryUserPasswordBlob;
+
+	void decode_PrimaryUserPasswordBlob(
+		[in] package_PrimaryUserPasswordBlob blob
+	);
+
 	typedef struct {
 		[value(0)] uint32 size;
 	} AuthInfoNone;
diff --git a/librpc/rpc/binding.c b/librpc/rpc/binding.c
index 51f89cc..63ba682 100644
--- a/librpc/rpc/binding.c
+++ b/librpc/rpc/binding.c
@@ -98,7 +98,7 @@ static const struct ncacn_option {
 	{"spnego", DCERPC_AUTH_SPNEGO},
 	{"ntlm", DCERPC_AUTH_NTLM},
 	{"krb5", DCERPC_AUTH_KRB5},
-	{"schannel", DCERPC_SCHANNEL},
+	{"schannel", DCERPC_SCHANNEL | DCERPC_SCHANNEL_AUTO},
 	{"validate", DCERPC_DEBUG_VALIDATE_BOTH},
 	{"print", DCERPC_DEBUG_PRINT_BOTH},
 	{"padcheck", DCERPC_DEBUG_PAD_CHECK},
diff --git a/python/samba/netcmd/user.py b/python/samba/netcmd/user.py
index f1e17dd..53ac39f 100644
--- a/python/samba/netcmd/user.py
+++ b/python/samba/netcmd/user.py
@@ -103,7 +103,7 @@ def get_random_bytes(num):
         raise ImportError(random_reason)
     return get_random_bytes_fn(num)
 
-def get_crypt_value(alg, utf8pw):
+def get_crypt_value(alg, utf8pw, rounds=0):
     algs = {
         "5": {"length": 43},
         "6": {"length": 86},
@@ -116,8 +116,13 @@ def get_crypt_value(alg, utf8pw):
     # we can ignore the possible == at the end
     # of the base64 string
     # we just need to replace '+' by '.'
-    b64salt = base64.b64encode(salt)
-    crypt_salt = "$%s$%s$" % (alg, b64salt[0:16].replace('+', '.'))
+    b64salt = base64.b64encode(salt)[0:16].replace('+', '.')
+    crypt_salt = ""
+    if rounds != 0:
+        crypt_salt = "$%s$rounds=%s$%s$" % (alg, rounds, b64salt)
+    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))
@@ -127,6 +132,24 @@ def get_crypt_value(alg, utf8pw):
             crypt_salt, len(crypt_value), expected_len))
     return crypt_value
 
+# Extract the rounds value from the options of a virtualCrypt attribute
+# i.e. options = "rounds=20;other=ignored;" will return 20
+# if the rounds option is not found or the value is not a number, 0 is returned
+# which indicates that the default number of rounds should be used.
+def get_rounds(options):
+    if not options:
+        return 0
+
+    opts = options.split(';')
+    for o in opts:
+        if o.lower().startswith("rounds="):
+            (key, _, val) = o.partition('=')
+            try:
+                return int(val)
+            except ValueError:
+                return 0
+    return 0
+
 try:
     random_reason = check_random()
     if random_reason is not None:
@@ -169,6 +192,10 @@ for (alg, attr) in [("5", "virtualCryptSHA256"), ("6", "virtualCryptSHA512")]:
             "reason" : reason,
             }
 
+# Add the wDigest virtual attributes, virtualWDigest01 to virtualWDigest29
+for x in range(1, 30):
+    virtual_attributes["virtualWDigest%02d" % x] = {}
+
 virtual_attributes_help  = "The attributes to display (comma separated). "
 virtual_attributes_help += "Possible supported virtual attributes: %s" % ", ".join(sorted(virtual_attributes.keys()))
 if len(disabled_virtual_attributes) != 0:
@@ -878,9 +905,19 @@ class GetPasswordCommand(Command):
     def get_account_attributes(self, samdb, username, basedn, filter, scope,
                                attrs, decrypt):
 
-        require_supplementalCredentials = False
-        search_attrs = attrs[:]
+        raw_attrs = attrs[:]
+        search_attrs = []
+        attr_opts = {}
+        for a in raw_attrs:
+            (attr, _, opts) = a.partition(';')
+            if opts:
+                attr_opts[attr] = opts
+            else:
+                attr_opts[attr] = None
+            search_attrs.append(attr)
         lower_attrs = [x.lower() for x in search_attrs]
+
+        require_supplementalCredentials = False
         for a in virtual_attributes.keys():
             if a.lower() in lower_attrs:
                 require_supplementalCredentials = True
@@ -901,6 +938,12 @@ class GetPasswordCommand(Command):
             search_attrs += [a]
             add_sAMAcountName = True
 
+        add_userPrincipalName = False
+        upn = "usePrincipalName"
+        if upn.lower() not in lower_attrs:
+            search_attrs += [upn]
+            add_userPrincipalName = True
+
         if scope == ldb.SCOPE_BASE:
             search_controls = ["show_deleted:1", "show_recycled:1"]
         else:
@@ -932,6 +975,13 @@ class GetPasswordCommand(Command):
         account_name = obj["sAMAccountName"][0]
         if add_sAMAcountName:
             del obj["sAMAccountName"]
+        if "userPrincipalName" in obj:
+            account_upn = obj["userPrincipalName"][0]
+        else:
+            realm = self.lp.get("realm")
+            account_upn = "%s@%s" % (account_name, realm.lower())
+        if add_userPrincipalName:
+            del obj["userPrincipalName"]
 
         calculated = {}
         def get_package(name, min_idx=0):
@@ -1002,6 +1052,168 @@ class GetPasswordCommand(Command):
             u8 = u.encode('utf-8')
             return u8
 
+        # Extract the WDigest hash for the value specified by i.
+        # Builds an htdigest compatible value
+        DIGEST = "Digest"
+        def get_wDigest(i, primary_wdigest, account_name, account_upn,
+                        domain, dns_domain):
+            if i == 1:
+                user  = account_name
+                realm= domain
+            elif i == 2:
+                user  = account_name.lower()
+                realm = domain.lower()
+            elif i == 3:
+                user  = account_name.upper()
+                realm = domain.upper()
+            elif i == 4:
+                user  = account_name
+                realm = domain.upper()
+            elif i == 5:
+                user  = account_name
+                realm = domain.lower()
+            elif i == 6:
+                user  = account_name.upper()
+                realm = domain.lower()
+            elif i == 7:
+                user  = account_name.lower()
+                realm = domain.upper()
+            elif i == 8:
+                user  = account_name
+                realm = dns_domain.lower()
+            elif i == 9:
+                user  = account_name.lower()
+                realm = dns_domain.lower()
+            elif i == 10:
+                user  = account_name.upper()
+                realm = dns_domain.upper()
+            elif i == 11:
+                user  = account_name
+                realm = dns_domain.upper()
+            elif i == 12:
+                user  = account_name
+                realm = dns_domain.lower()
+            elif i == 13:
+                user  = account_name.upper()
+                realm = dns_domain.lower()
+            elif i == 14:
+                user  = account_name.lower()
+                realm = dns_domain.upper()
+            elif i == 15:
+                user  = account_upn
+                realm = ""
+            elif i == 16:
+                user  = account_upn.lower()
+                realm = ""
+            elif i == 17:
+                user  = account_upn.upper()
+                realm = ""
+            elif i == 18:
+                user  = "%s\\%s" % (domain, account_name)
+                realm = ""
+            elif i == 19:
+                user  = "%s\\%s" % (domain.lower(), account_name.lower())
+                realm = ""
+            elif i == 20:
+                user  = "%s\\%s" % (domain.upper(), account_name.upper())
+                realm = ""
+            elif i == 21:
+                user  = account_name
+                realm = DIGEST
+            elif i == 22:
+                user  = account_name.lower()
+                realm = DIGEST
+            elif i == 23:
+                user  = account_name.upper()
+                realm = DIGEST
+            elif i == 24:
+                user  = account_upn
+                realm = DIGEST
+            elif i == 25:
+                user  = account_upn.lower()
+                realm = DIGEST
+            elif i == 26:
+                user  = account_upn.upper()
+                realm = DIGEST
+            elif i == 27:
+                user  = "%s\\%s" % (domain, account_name)
+                realm = DIGEST
+            elif i == 28:
+                # Differs from spec, see tests
+                user  = "%s\\%s" % (domain.lower(), account_name.lower())
+                realm = DIGEST
+            elif i == 29:
+                # Differs from spec, see tests
+                user  = "%s\\%s" % (domain.upper(), account_name.upper())
+                realm = DIGEST
+            else:
+                user  = ""
+
+            digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob,
+                                 primary_wdigest)
+            try:
+                digest = binascii.hexlify(bytearray(digests.hashes[i-1].hash))
+                return "%s:%s:%s" % (user, realm, digest)
+            except IndexError:
+                return None
+
+
+        # get the value for a virtualCrypt attribute.
+        # look for an exact match on algorithm and rounds in supplemental creds
+        # if not found calculate using Primary:CLEARTEXT
+        # if no Primary:CLEARTEXT return the first supplementalCredential
+        #    that matches the algorithm.
+        def get_virtual_crypt_value(a, algorithm, rounds, username, account_name):
+            sv = None
+            fb = None
+            b = get_package("Primary:userPassword")
+            if b is not None:
+                (sv, fb) = get_userPassword_hash(b, algorithm, rounds)
+            if sv is None:
+                # No exact match on algorithm and number of rounds
+                # try and calculate one from the Primary:CLEARTEXT
+                b = get_package("Primary:CLEARTEXT")
+                if b is not None:
+                    u8 = get_utf8(a, b, username or account_name)
+                    if u8 is not None:
+                        sv = get_crypt_value(str(algorithm), u8, rounds)
+                if sv is None:
+                    # Unable to calculate a hash with the specified
+                    # number of rounds, fall back to the first hash using
+                    # the specified algorithm
+                    sv = fb
+            if sv is None:
+                return None
+            return "{CRYPT}" + sv
+
+        def get_userPassword_hash(blob, algorithm, rounds):
+            up = ndr_unpack(drsblobs.package_PrimaryUserPasswordBlob, blob)
+            SCHEME = "{CRYPT}"
+
+            # Check that the NT hash has not been changed without updating
+            # the user password hashes. This indicates that password has been
+            # changed without updating the supplemental credentials.
+            if unicodePwd != bytearray(up.current_nt_hash.hash):
+                return None
+
+            scheme_prefix = "$%d$" % algorithm
+            prefix = scheme_prefix
+            if rounds > 0:
+                prefix = "$%d$rounds=%d" % (algorithm, rounds)
+            scheme_match = None
+
+            for h in up.hashes:
+                if (scheme_match is None and
+                      h.scheme == SCHEME and
+                      h.value.startswith(scheme_prefix)):
+                    scheme_match = h.value
+                if h.scheme == SCHEME and h.value.startswith(prefix):
+                    return (h.value, scheme_match)
+
+            # No match on the number of rounds, return the value of the
+            # first matching scheme
+            return (None, scheme_match)
+
         # We use sort here in order to have a predictable processing order
         for a in sorted(virtual_attributes.keys()):
             if not a.lower() in lower_attrs:


-- 
Samba Shared Repository



More information about the samba-cvs mailing list