summaryrefslogtreecommitdiffstats
path: root/libcli/auth
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libcli/auth/credentials.c1246
-rw-r--r--libcli/auth/credentials.h70
-rw-r--r--libcli/auth/libcli_auth.h28
-rw-r--r--libcli/auth/msrpc_parse.c409
-rw-r--r--libcli/auth/msrpc_parse.h58
-rw-r--r--libcli/auth/netlogon_creds_cli.c4254
-rw-r--r--libcli/auth/netlogon_creds_cli.h236
-rw-r--r--libcli/auth/ntlm_check.c648
-rw-r--r--libcli/auth/ntlm_check.h86
-rw-r--r--libcli/auth/pam_errors.c143
-rw-r--r--libcli/auth/pam_errors.h33
-rw-r--r--libcli/auth/proto.h296
-rw-r--r--libcli/auth/schannel.h25
-rw-r--r--libcli/auth/schannel_proto.h31
-rw-r--r--libcli/auth/schannel_state.h54
-rw-r--r--libcli/auth/schannel_state_tdb.c646
-rw-r--r--libcli/auth/session.c243
-rw-r--r--libcli/auth/smbdes.c213
-rw-r--r--libcli/auth/smbencrypt.c1325
-rw-r--r--libcli/auth/spnego.h82
-rw-r--r--libcli/auth/spnego_parse.c446
-rw-r--r--libcli/auth/spnego_proto.h28
-rw-r--r--libcli/auth/tests/ntlm_check.c413
-rw-r--r--libcli/auth/tests/test_encode_decode.c162
-rw-r--r--libcli/auth/tests/test_gnutls.c528
-rw-r--r--libcli/auth/tests/test_rc4_passwd_buffer.c336
-rw-r--r--libcli/auth/tests/test_schannel.c305
-rw-r--r--libcli/auth/wscript_build100
28 files changed, 12444 insertions, 0 deletions
diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c
new file mode 100644
index 0000000..a7f56e7
--- /dev/null
+++ b/libcli/auth/credentials.c
@@ -0,0 +1,1246 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ code to manipulate domain credentials
+
+ Copyright (C) Andrew Tridgell 1997-2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include "libcli/auth/libcli_auth.h"
+#include "../libcli/security/dom_sid.h"
+#include "lib/util/util_str_escape.h"
+
+#ifndef HAVE_GNUTLS_AES_CFB8
+#include "lib/crypto/aes.h"
+#endif
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+bool netlogon_creds_is_random_challenge(const struct netr_Credential *challenge)
+{
+ /*
+ * If none of the first 5 bytes of the client challenge is unique, the
+ * server MUST fail session-key negotiation without further processing
+ * of the following steps.
+ */
+
+ if (challenge->data[1] == challenge->data[0] &&
+ challenge->data[2] == challenge->data[0] &&
+ challenge->data[3] == challenge->data[0] &&
+ challenge->data[4] == challenge->data[0])
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void netlogon_creds_random_challenge(struct netr_Credential *challenge)
+{
+ ZERO_STRUCTP(challenge);
+ while (!netlogon_creds_is_random_challenge(challenge)) {
+ generate_random_buffer(challenge->data, sizeof(challenge->data));
+ }
+}
+
+static NTSTATUS netlogon_creds_step_crypt(struct netlogon_creds_CredentialState *creds,
+ const struct netr_Credential *in,
+ struct netr_Credential *out)
+{
+ NTSTATUS status;
+ int rc;
+
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ memcpy(out->data, in->data, sizeof(out->data));
+
+ status = netlogon_creds_aes_encrypt(creds,
+ out->data,
+ sizeof(out->data));
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else {
+ rc = des_crypt112(out->data, in->data, creds->session_key, SAMBA_GNUTLS_ENCRYPT);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc,
+ NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ initialise the credentials state for old-style 64 bit session keys
+
+ this call is made after the netr_ServerReqChallenge call
+*/
+static NTSTATUS netlogon_creds_init_64bit(struct netlogon_creds_CredentialState *creds,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const struct samr_Password *machine_password)
+{
+ uint32_t sum[2];
+ uint8_t sum2[8];
+ int rc;
+
+ sum[0] = IVAL(client_challenge->data, 0) + IVAL(server_challenge->data, 0);
+ sum[1] = IVAL(client_challenge->data, 4) + IVAL(server_challenge->data, 4);
+
+ SIVAL(sum2,0,sum[0]);
+ SIVAL(sum2,4,sum[1]);
+
+ ZERO_ARRAY(creds->session_key);
+
+ rc = des_crypt128(creds->session_key, sum2, machine_password->hash);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ initialise the credentials state for ADS-style 128 bit session keys
+
+ this call is made after the netr_ServerReqChallenge call
+*/
+static NTSTATUS netlogon_creds_init_128bit(struct netlogon_creds_CredentialState *creds,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const struct samr_Password *machine_password)
+{
+ uint8_t zero[4] = {0};
+ uint8_t tmp[gnutls_hash_get_len(GNUTLS_MAC_MD5)];
+ gnutls_hash_hd_t hash_hnd = NULL;
+ int rc;
+
+ ZERO_ARRAY(creds->session_key);
+
+ rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ }
+
+ rc = gnutls_hash(hash_hnd, zero, sizeof(zero));
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ }
+ rc = gnutls_hash(hash_hnd, client_challenge->data, 8);
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ }
+ rc = gnutls_hash(hash_hnd, server_challenge->data, 8);
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ }
+
+ gnutls_hash_deinit(hash_hnd, tmp);
+
+ /* This doesn't require HMAC MD5 RFC2104 as the hash is only 16 bytes */
+ rc = gnutls_hmac_fast(GNUTLS_MAC_MD5,
+ machine_password->hash,
+ sizeof(machine_password->hash),
+ tmp,
+ sizeof(tmp),
+ creds->session_key);
+ ZERO_ARRAY(tmp);
+
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ initialise the credentials state for AES/HMAC-SHA256-style 128 bit session keys
+
+ this call is made after the netr_ServerReqChallenge call
+*/
+static NTSTATUS netlogon_creds_init_hmac_sha256(struct netlogon_creds_CredentialState *creds,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const struct samr_Password *machine_password)
+{
+ gnutls_hmac_hd_t hmac_hnd = NULL;
+ uint8_t digest[gnutls_hash_get_len(GNUTLS_MAC_SHA256)];
+ int rc;
+
+ ZERO_ARRAY(creds->session_key);
+
+ rc = gnutls_hmac_init(&hmac_hnd,
+ GNUTLS_MAC_SHA256,
+ machine_password->hash,
+ sizeof(machine_password->hash));
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED);
+ }
+ rc = gnutls_hmac(hmac_hnd,
+ client_challenge->data,
+ 8);
+ if (rc < 0) {
+ gnutls_hmac_deinit(hmac_hnd, NULL);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED);
+ }
+ rc = gnutls_hmac(hmac_hnd,
+ server_challenge->data,
+ 8);
+ if (rc < 0) {
+ gnutls_hmac_deinit(hmac_hnd, NULL);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED);
+ }
+ gnutls_hmac_deinit(hmac_hnd, digest);
+
+ memcpy(creds->session_key, digest, sizeof(creds->session_key));
+
+ ZERO_ARRAY(digest);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS netlogon_creds_first_step(struct netlogon_creds_CredentialState *creds,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge)
+{
+ NTSTATUS status;
+
+ status = netlogon_creds_step_crypt(creds,
+ client_challenge,
+ &creds->client);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = netlogon_creds_step_crypt(creds,
+ server_challenge,
+ &creds->server);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ creds->seed = creds->client;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ step the credentials to the next element in the chain, updating the
+ current client and server credentials and the seed
+*/
+static NTSTATUS netlogon_creds_step(struct netlogon_creds_CredentialState *creds)
+{
+ struct netr_Credential time_cred;
+ NTSTATUS status;
+
+ DEBUG(5,("\tseed %08x:%08x\n",
+ IVAL(creds->seed.data, 0), IVAL(creds->seed.data, 4)));
+
+ SIVAL(time_cred.data, 0, IVAL(creds->seed.data, 0) + creds->sequence);
+ SIVAL(time_cred.data, 4, IVAL(creds->seed.data, 4));
+
+ DEBUG(5,("\tseed+time %08x:%08x\n", IVAL(time_cred.data, 0), IVAL(time_cred.data, 4)));
+
+ status = netlogon_creds_step_crypt(creds,
+ &time_cred,
+ &creds->client);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(5,("\tCLIENT %08x:%08x\n",
+ IVAL(creds->client.data, 0), IVAL(creds->client.data, 4)));
+
+ SIVAL(time_cred.data, 0, IVAL(creds->seed.data, 0) + creds->sequence + 1);
+ SIVAL(time_cred.data, 4, IVAL(creds->seed.data, 4));
+
+ DEBUG(5,("\tseed+time+1 %08x:%08x\n",
+ IVAL(time_cred.data, 0), IVAL(time_cred.data, 4)));
+
+ status = netlogon_creds_step_crypt(creds, &time_cred, &creds->server);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(5,("\tSERVER %08x:%08x\n",
+ IVAL(creds->server.data, 0), IVAL(creds->server.data, 4)));
+
+ creds->seed = time_cred;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ DES encrypt a 8 byte LMSessionKey buffer using the Netlogon session key
+*/
+NTSTATUS netlogon_creds_des_encrypt_LMKey(struct netlogon_creds_CredentialState *creds,
+ struct netr_LMSessionKey *key)
+{
+ int rc;
+ struct netr_LMSessionKey tmp;
+
+ rc = des_crypt56_gnutls(tmp.key, key->key, creds->session_key, SAMBA_GNUTLS_ENCRYPT);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+ *key = tmp;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ DES decrypt a 8 byte LMSessionKey buffer using the Netlogon session key
+*/
+NTSTATUS netlogon_creds_des_decrypt_LMKey(struct netlogon_creds_CredentialState *creds,
+ struct netr_LMSessionKey *key)
+{
+ int rc;
+ struct netr_LMSessionKey tmp;
+
+ rc = des_crypt56_gnutls(tmp.key, key->key, creds->session_key, SAMBA_GNUTLS_DECRYPT);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+ *key = tmp;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ DES encrypt a 16 byte password buffer using the session key
+*/
+NTSTATUS netlogon_creds_des_encrypt(struct netlogon_creds_CredentialState *creds,
+ struct samr_Password *pass)
+{
+ struct samr_Password tmp;
+ int rc;
+
+ rc = des_crypt112_16(tmp.hash, pass->hash, creds->session_key, SAMBA_GNUTLS_ENCRYPT);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+ *pass = tmp;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ DES decrypt a 16 byte password buffer using the session key
+*/
+NTSTATUS netlogon_creds_des_decrypt(struct netlogon_creds_CredentialState *creds,
+ struct samr_Password *pass)
+{
+ struct samr_Password tmp;
+ int rc;
+
+ rc = des_crypt112_16(tmp.hash, pass->hash, creds->session_key, SAMBA_GNUTLS_DECRYPT);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+ *pass = tmp;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ ARCFOUR encrypt/decrypt a password buffer using the session key
+*/
+NTSTATUS netlogon_creds_arcfour_crypt(struct netlogon_creds_CredentialState *creds,
+ uint8_t *data,
+ size_t len)
+{
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t session_key = {
+ .data = creds->session_key,
+ .size = sizeof(creds->session_key),
+ };
+ int rc;
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &session_key,
+ NULL);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc,
+ NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ data,
+ len);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc,
+ NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ AES encrypt a password buffer using the session key
+*/
+NTSTATUS netlogon_creds_aes_encrypt(struct netlogon_creds_CredentialState *creds,
+ uint8_t *data,
+ size_t len)
+{
+#ifdef HAVE_GNUTLS_AES_CFB8
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t key = {
+ .data = creds->session_key,
+ .size = sizeof(creds->session_key),
+ };
+ uint32_t iv_size =
+ gnutls_cipher_get_iv_size(GNUTLS_CIPHER_AES_128_CFB8);
+ uint8_t _iv[iv_size];
+ gnutls_datum_t iv = {
+ .data = _iv,
+ .size = iv_size,
+ };
+ int rc;
+
+ ZERO_ARRAY(_iv);
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_AES_128_CFB8,
+ &key,
+ &iv);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+
+ rc = gnutls_cipher_encrypt(cipher_hnd, data, len);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+
+#else /* NOT HAVE_GNUTLS_AES_CFB8 */
+ AES_KEY key;
+ uint8_t iv[AES_BLOCK_SIZE] = {0};
+
+ AES_set_encrypt_key(creds->session_key, 128, &key);
+
+ aes_cfb8_encrypt(data, data, len, &key, iv, AES_ENCRYPT);
+#endif /* HAVE_GNUTLS_AES_CFB8 */
+
+ return NT_STATUS_OK;
+}
+
+/*
+ AES decrypt a password buffer using the session key
+*/
+NTSTATUS netlogon_creds_aes_decrypt(struct netlogon_creds_CredentialState *creds, uint8_t *data, size_t len)
+{
+#ifdef HAVE_GNUTLS_AES_CFB8
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t key = {
+ .data = creds->session_key,
+ .size = sizeof(creds->session_key),
+ };
+ uint32_t iv_size =
+ gnutls_cipher_get_iv_size(GNUTLS_CIPHER_AES_128_CFB8);
+ uint8_t _iv[iv_size];
+ gnutls_datum_t iv = {
+ .data = _iv,
+ .size = iv_size,
+ };
+ int rc;
+
+ ZERO_ARRAY(_iv);
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_AES_128_CFB8,
+ &key,
+ &iv);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc,
+ NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+
+ rc = gnutls_cipher_decrypt(cipher_hnd, data, len);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc,
+ NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+
+#else /* NOT HAVE_GNUTLS_AES_CFB8 */
+ AES_KEY key;
+ uint8_t iv[AES_BLOCK_SIZE] = {0};
+
+ AES_set_encrypt_key(creds->session_key, 128, &key);
+
+ aes_cfb8_encrypt(data, data, len, &key, iv, AES_DECRYPT);
+#endif /* HAVE_GNUTLS_AES_CFB8 */
+
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************
+The above functions are common to the client and server interface
+next comes the client specific functions
+******************************************************************/
+
+/*
+ initialise the credentials chain and return the first client
+ credentials
+*/
+
+struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *mem_ctx,
+ const char *client_account,
+ const char *client_computer_name,
+ uint16_t secure_channel_type,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const struct samr_Password *machine_password,
+ struct netr_Credential *initial_credential,
+ uint32_t negotiate_flags)
+{
+ struct netlogon_creds_CredentialState *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
+ NTSTATUS status;
+
+ if (!creds) {
+ return NULL;
+ }
+
+ creds->sequence = time(NULL);
+ creds->negotiate_flags = negotiate_flags;
+ creds->secure_channel_type = secure_channel_type;
+
+ creds->computer_name = talloc_strdup(creds, client_computer_name);
+ if (!creds->computer_name) {
+ talloc_free(creds);
+ return NULL;
+ }
+ creds->account_name = talloc_strdup(creds, client_account);
+ if (!creds->account_name) {
+ talloc_free(creds);
+ return NULL;
+ }
+
+ dump_data_pw("Client chall", client_challenge->data, sizeof(client_challenge->data));
+ dump_data_pw("Server chall", server_challenge->data, sizeof(server_challenge->data));
+ dump_data_pw("Machine Pass", machine_password->hash, sizeof(machine_password->hash));
+
+ if (negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ status = netlogon_creds_init_hmac_sha256(creds,
+ client_challenge,
+ server_challenge,
+ machine_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(creds);
+ return NULL;
+ }
+ } else if (negotiate_flags & NETLOGON_NEG_STRONG_KEYS) {
+ status = netlogon_creds_init_128bit(creds,
+ client_challenge,
+ server_challenge,
+ machine_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(creds);
+ return NULL;
+ }
+ } else {
+ status = netlogon_creds_init_64bit(creds,
+ client_challenge,
+ server_challenge,
+ machine_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(creds);
+ return NULL;
+ }
+ }
+
+ status = netlogon_creds_first_step(creds,
+ client_challenge,
+ server_challenge);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(creds);
+ return NULL;
+ }
+
+ dump_data_pw("Session key", creds->session_key, 16);
+ dump_data_pw("Credential ", creds->client.data, 8);
+
+ *initial_credential = creds->client;
+ return creds;
+}
+
+/*
+ initialise the credentials structure with only a session key. The caller better know what they are doing!
+ */
+
+struct netlogon_creds_CredentialState *netlogon_creds_client_init_session_key(TALLOC_CTX *mem_ctx,
+ const uint8_t session_key[16])
+{
+ struct netlogon_creds_CredentialState *creds;
+
+ creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
+ if (!creds) {
+ return NULL;
+ }
+
+ memcpy(creds->session_key, session_key, 16);
+
+ return creds;
+}
+
+/*
+ step the credentials to the next element in the chain, updating the
+ current client and server credentials and the seed
+
+ produce the next authenticator in the sequence ready to send to
+ the server
+*/
+NTSTATUS
+netlogon_creds_client_authenticator(struct netlogon_creds_CredentialState *creds,
+ struct netr_Authenticator *next)
+{
+ uint32_t t32n = (uint32_t)time(NULL);
+ NTSTATUS status;
+
+ /*
+ * we always increment and ignore an overflow here
+ */
+ creds->sequence += 2;
+
+ if (t32n > creds->sequence) {
+ /*
+ * we may increment more
+ */
+ creds->sequence = t32n;
+ } else {
+ uint32_t d = creds->sequence - t32n;
+
+ if (d >= INT32_MAX) {
+ /*
+ * got an overflow of time_t vs. uint32_t
+ */
+ creds->sequence = t32n;
+ }
+ }
+
+ status = netlogon_creds_step(creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ next->cred = creds->client;
+ next->timestamp = creds->sequence;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ check that a credentials reply from a server is correct
+*/
+bool netlogon_creds_client_check(struct netlogon_creds_CredentialState *creds,
+ const struct netr_Credential *received_credentials)
+{
+ if (!received_credentials ||
+ !mem_equal_const_time(received_credentials->data, creds->server.data, 8)) {
+ DEBUG(2,("credentials check failed\n"));
+ return false;
+ }
+ return true;
+}
+
+
+/*****************************************************************
+The above functions are common to the client and server interface
+next comes the server specific functions
+******************************************************************/
+
+/*
+ check that a credentials reply from a server is correct
+*/
+static bool netlogon_creds_server_check_internal(const struct netlogon_creds_CredentialState *creds,
+ const struct netr_Credential *received_credentials)
+{
+ if (!mem_equal_const_time(received_credentials->data, creds->client.data, 8)) {
+ DEBUG(2,("credentials check failed\n"));
+ dump_data_pw("client creds", creds->client.data, 8);
+ dump_data_pw("calc creds", received_credentials->data, 8);
+ return false;
+ }
+ return true;
+}
+
+/*
+ initialise the credentials chain and return the first server
+ credentials
+*/
+struct netlogon_creds_CredentialState *netlogon_creds_server_init(TALLOC_CTX *mem_ctx,
+ const char *client_account,
+ const char *client_computer_name,
+ uint16_t secure_channel_type,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const struct samr_Password *machine_password,
+ const struct netr_Credential *credentials_in,
+ struct netr_Credential *credentials_out,
+ uint32_t negotiate_flags)
+{
+
+ struct netlogon_creds_CredentialState *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
+ NTSTATUS status;
+ bool ok;
+
+ if (!creds) {
+ return NULL;
+ }
+
+ creds->negotiate_flags = negotiate_flags;
+ creds->secure_channel_type = secure_channel_type;
+
+ dump_data_pw("Client chall", client_challenge->data, sizeof(client_challenge->data));
+ dump_data_pw("Server chall", server_challenge->data, sizeof(server_challenge->data));
+ dump_data_pw("Machine Pass", machine_password->hash, sizeof(machine_password->hash));
+
+ ok = netlogon_creds_is_random_challenge(client_challenge);
+ if (!ok) {
+ DBG_WARNING("CVE-2020-1472(ZeroLogon): "
+ "non-random client challenge rejected for "
+ "client_account[%s] client_computer_name[%s]\n",
+ log_escape(mem_ctx, client_account),
+ log_escape(mem_ctx, client_computer_name));
+ dump_data(DBGLVL_WARNING,
+ client_challenge->data,
+ sizeof(client_challenge->data));
+ talloc_free(creds);
+ return NULL;
+ }
+
+ creds->computer_name = talloc_strdup(creds, client_computer_name);
+ if (!creds->computer_name) {
+ talloc_free(creds);
+ return NULL;
+ }
+ creds->account_name = talloc_strdup(creds, client_account);
+ if (!creds->account_name) {
+ talloc_free(creds);
+ return NULL;
+ }
+
+ if (negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ status = netlogon_creds_init_hmac_sha256(creds,
+ client_challenge,
+ server_challenge,
+ machine_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(creds);
+ return NULL;
+ }
+ } else if (negotiate_flags & NETLOGON_NEG_STRONG_KEYS) {
+ status = netlogon_creds_init_128bit(creds,
+ client_challenge,
+ server_challenge,
+ machine_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(creds);
+ return NULL;
+ }
+ } else {
+ status = netlogon_creds_init_64bit(creds,
+ client_challenge,
+ server_challenge,
+ machine_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(creds);
+ return NULL;
+ }
+ }
+
+ status = netlogon_creds_first_step(creds,
+ client_challenge,
+ server_challenge);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(creds);
+ return NULL;
+ }
+
+ dump_data_pw("Session key", creds->session_key, 16);
+ dump_data_pw("Client Credential ", creds->client.data, 8);
+ dump_data_pw("Server Credential ", creds->server.data, 8);
+
+ dump_data_pw("Credentials in", credentials_in->data, sizeof(credentials_in->data));
+
+ /* And before we leak information about the machine account
+ * password, check that they got the first go right */
+ if (!netlogon_creds_server_check_internal(creds, credentials_in)) {
+ talloc_free(creds);
+ return NULL;
+ }
+
+ *credentials_out = creds->server;
+
+ dump_data_pw("Credentials out", credentials_out->data, sizeof(credentials_out->data));
+
+ return creds;
+}
+
+NTSTATUS netlogon_creds_server_step_check(struct netlogon_creds_CredentialState *creds,
+ const struct netr_Authenticator *received_authenticator,
+ struct netr_Authenticator *return_authenticator)
+{
+ NTSTATUS status;
+
+ if (!received_authenticator || !return_authenticator) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!creds) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ creds->sequence = received_authenticator->timestamp;
+ status = netlogon_creds_step(creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(return_authenticator);
+ return status;
+ }
+
+ if (netlogon_creds_server_check_internal(creds, &received_authenticator->cred)) {
+ return_authenticator->cred = creds->server;
+ return_authenticator->timestamp = 0;
+ return NT_STATUS_OK;
+ } else {
+ ZERO_STRUCTP(return_authenticator);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+}
+
+static NTSTATUS netlogon_creds_crypt_samlogon_validation(struct netlogon_creds_CredentialState *creds,
+ uint16_t validation_level,
+ union netr_Validation *validation,
+ bool do_encrypt)
+{
+ struct netr_SamBaseInfo *base = NULL;
+ NTSTATUS status;
+
+ if (validation == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (validation_level) {
+ case 2:
+ if (validation->sam2) {
+ base = &validation->sam2->base;
+ }
+ break;
+ case 3:
+ if (validation->sam3) {
+ base = &validation->sam3->base;
+ }
+ break;
+ case 6:
+ if (validation->sam6) {
+ base = &validation->sam6->base;
+ }
+ break;
+ default:
+ /* If we can't find it, we can't very well decrypt it */
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (!base) {
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* find and decyrpt the session keys, return in parameters above */
+ if (validation_level == 6) {
+ /* they aren't encrypted! */
+ } else if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ /* Don't crypt an all-zero key, it would give away the NETLOGON pipe session key */
+ if (!all_zero(base->key.key, sizeof(base->key.key))) {
+ if (do_encrypt) {
+ status = netlogon_creds_aes_encrypt(
+ creds,
+ base->key.key,
+ sizeof(base->key.key));
+ } else {
+ status = netlogon_creds_aes_decrypt(
+ creds,
+ base->key.key,
+ sizeof(base->key.key));
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ if (!all_zero(base->LMSessKey.key,
+ sizeof(base->LMSessKey.key))) {
+ if (do_encrypt) {
+ status = netlogon_creds_aes_encrypt(
+ creds,
+ base->LMSessKey.key,
+ sizeof(base->LMSessKey.key));
+ } else {
+ status = netlogon_creds_aes_decrypt(
+ creds,
+ base->LMSessKey.key,
+ sizeof(base->LMSessKey.key));
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ /* Don't crypt an all-zero key, it would give away the NETLOGON pipe session key */
+ if (!all_zero(base->key.key, sizeof(base->key.key))) {
+ status = netlogon_creds_arcfour_crypt(creds,
+ base->key.key,
+ sizeof(base->key.key));
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ if (!all_zero(base->LMSessKey.key,
+ sizeof(base->LMSessKey.key))) {
+ status = netlogon_creds_arcfour_crypt(creds,
+ base->LMSessKey.key,
+ sizeof(base->LMSessKey.key));
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ } else {
+ /* Don't crypt an all-zero key, it would give away the NETLOGON pipe session key */
+ if (!all_zero(base->LMSessKey.key,
+ sizeof(base->LMSessKey.key))) {
+ if (do_encrypt) {
+ status = netlogon_creds_des_encrypt_LMKey(creds,
+ &base->LMSessKey);
+ } else {
+ status = netlogon_creds_des_decrypt_LMKey(creds,
+ &base->LMSessKey);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_decrypt_samlogon_validation(struct netlogon_creds_CredentialState *creds,
+ uint16_t validation_level,
+ union netr_Validation *validation)
+{
+ return netlogon_creds_crypt_samlogon_validation(creds,
+ validation_level,
+ validation,
+ false);
+}
+
+NTSTATUS netlogon_creds_encrypt_samlogon_validation(struct netlogon_creds_CredentialState *creds,
+ uint16_t validation_level,
+ union netr_Validation *validation)
+{
+ return netlogon_creds_crypt_samlogon_validation(creds,
+ validation_level,
+ validation,
+ true);
+}
+
+static NTSTATUS netlogon_creds_crypt_samlogon_logon(struct netlogon_creds_CredentialState *creds,
+ enum netr_LogonInfoClass level,
+ union netr_LogonLevel *logon,
+ bool do_encrypt)
+{
+ NTSTATUS status;
+
+ if (logon == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonServiceTransitiveInformation:
+ if (logon->password == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ uint8_t *h;
+
+ h = logon->password->lmpassword.hash;
+ if (!all_zero(h, 16)) {
+ if (do_encrypt) {
+ status = netlogon_creds_aes_encrypt(
+ creds,
+ h,
+ 16);
+ } else {
+ status = netlogon_creds_aes_decrypt(
+ creds,
+ h,
+ 16);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ h = logon->password->ntpassword.hash;
+ if (!all_zero(h, 16)) {
+ if (do_encrypt) {
+ status = netlogon_creds_aes_encrypt(creds,
+ h,
+ 16);
+ } else {
+ status = netlogon_creds_aes_decrypt(creds,
+ h,
+ 16);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ uint8_t *h;
+
+ h = logon->password->lmpassword.hash;
+ if (!all_zero(h, 16)) {
+ status = netlogon_creds_arcfour_crypt(creds,
+ h,
+ 16);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ h = logon->password->ntpassword.hash;
+ if (!all_zero(h, 16)) {
+ status = netlogon_creds_arcfour_crypt(creds,
+ h,
+ 16);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ } else {
+ struct samr_Password *p;
+
+ p = &logon->password->lmpassword;
+ if (!all_zero(p->hash, 16)) {
+ if (do_encrypt) {
+ status = netlogon_creds_des_encrypt(creds, p);
+ } else {
+ status = netlogon_creds_des_decrypt(creds, p);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ p = &logon->password->ntpassword;
+ if (!all_zero(p->hash, 16)) {
+ if (do_encrypt) {
+ status = netlogon_creds_des_encrypt(creds, p);
+ } else {
+ status = netlogon_creds_des_decrypt(creds, p);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ }
+ break;
+
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+ break;
+
+ case NetlogonGenericInformation:
+ if (logon->generic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ if (do_encrypt) {
+ status = netlogon_creds_aes_encrypt(
+ creds,
+ logon->generic->data,
+ logon->generic->length);
+ } else {
+ status = netlogon_creds_aes_decrypt(
+ creds,
+ logon->generic->data,
+ logon->generic->length);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ status = netlogon_creds_arcfour_crypt(creds,
+ logon->generic->data,
+ logon->generic->length);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else {
+ /* Using DES to verify kerberos tickets makes no sense */
+ }
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_decrypt_samlogon_logon(struct netlogon_creds_CredentialState *creds,
+ enum netr_LogonInfoClass level,
+ union netr_LogonLevel *logon)
+{
+ return netlogon_creds_crypt_samlogon_logon(creds, level, logon, false);
+}
+
+NTSTATUS netlogon_creds_encrypt_samlogon_logon(struct netlogon_creds_CredentialState *creds,
+ enum netr_LogonInfoClass level,
+ union netr_LogonLevel *logon)
+{
+ return netlogon_creds_crypt_samlogon_logon(creds, level, logon, true);
+}
+
+union netr_LogonLevel *netlogon_creds_shallow_copy_logon(TALLOC_CTX *mem_ctx,
+ enum netr_LogonInfoClass level,
+ const union netr_LogonLevel *in)
+{
+ union netr_LogonLevel *out;
+
+ if (in == NULL) {
+ return NULL;
+ }
+
+ out = talloc(mem_ctx, union netr_LogonLevel);
+ if (out == NULL) {
+ return NULL;
+ }
+
+ *out = *in;
+
+ switch (level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonServiceTransitiveInformation:
+ if (in->password == NULL) {
+ return out;
+ }
+
+ out->password = talloc(out, struct netr_PasswordInfo);
+ if (out->password == NULL) {
+ talloc_free(out);
+ return NULL;
+ }
+ *out->password = *in->password;
+
+ return out;
+
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+ break;
+
+ case NetlogonGenericInformation:
+ if (in->generic == NULL) {
+ return out;
+ }
+
+ out->generic = talloc(out, struct netr_GenericInfo);
+ if (out->generic == NULL) {
+ talloc_free(out);
+ return NULL;
+ }
+ *out->generic = *in->generic;
+
+ if (in->generic->data == NULL) {
+ return out;
+ }
+
+ if (in->generic->length == 0) {
+ return out;
+ }
+
+ out->generic->data = talloc_memdup(out->generic,
+ in->generic->data,
+ in->generic->length);
+ if (out->generic->data == NULL) {
+ talloc_free(out);
+ return NULL;
+ }
+
+ return out;
+ }
+
+ return out;
+}
+
+/*
+ copy a netlogon_creds_CredentialState struct
+*/
+
+struct netlogon_creds_CredentialState *netlogon_creds_copy(
+ TALLOC_CTX *mem_ctx,
+ const struct netlogon_creds_CredentialState *creds_in)
+{
+ struct netlogon_creds_CredentialState *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
+
+ if (!creds) {
+ return NULL;
+ }
+
+ creds->sequence = creds_in->sequence;
+ creds->negotiate_flags = creds_in->negotiate_flags;
+ creds->secure_channel_type = creds_in->secure_channel_type;
+
+ creds->computer_name = talloc_strdup(creds, creds_in->computer_name);
+ if (!creds->computer_name) {
+ talloc_free(creds);
+ return NULL;
+ }
+ creds->account_name = talloc_strdup(creds, creds_in->account_name);
+ if (!creds->account_name) {
+ talloc_free(creds);
+ return NULL;
+ }
+
+ if (creds_in->sid) {
+ creds->sid = dom_sid_dup(creds, creds_in->sid);
+ if (!creds->sid) {
+ talloc_free(creds);
+ return NULL;
+ }
+ }
+
+ memcpy(creds->session_key, creds_in->session_key, sizeof(creds->session_key));
+ memcpy(creds->seed.data, creds_in->seed.data, sizeof(creds->seed.data));
+ memcpy(creds->client.data, creds_in->client.data, sizeof(creds->client.data));
+ memcpy(creds->server.data, creds_in->server.data, sizeof(creds->server.data));
+
+ return creds;
+}
diff --git a/libcli/auth/credentials.h b/libcli/auth/credentials.h
new file mode 100644
index 0000000..7b8fac6
--- /dev/null
+++ b/libcli/auth/credentials.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ code to manipulate domain credentials
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "librpc/gen_ndr/netlogon.h"
+
+/* The 7 here seems to be required to get Win2k not to downgrade us
+ to NT4. Actually, anything other than 1ff would seem to do... */
+#define NETLOGON_NEG_AUTH2_FLAGS 0x000701ff
+/*
+ (NETLOGON_NEG_ACCOUNT_LOCKOUT |
+ NETLOGON_NEG_PERSISTENT_SAMREPL |
+ NETLOGON_NEG_ARCFOUR |
+ NETLOGON_NEG_PROMOTION_COUNT |
+ NETLOGON_NEG_CHANGELOG_BDC |
+ NETLOGON_NEG_FULL_SYNC_REPL |
+ NETLOGON_NEG_MULTIPLE_SIDS |
+ NETLOGON_NEG_REDO |
+ NETLOGON_NEG_PASSWORD_CHANGE_REFUSAL |
+ NETLOGON_NEG_DNS_DOMAIN_TRUSTS |
+ NETLOGON_NEG_PASSWORD_SET2 |
+ NETLOGON_NEG_GETDOMAININFO)
+*/
+#define NETLOGON_NEG_DOMAIN_TRUST_ACCOUNT 0x2010b000
+
+/* these are the flags that ADS clients use */
+/*
+ (NETLOGON_NEG_ACCOUNT_LOCKOUT |
+ NETLOGON_NEG_PERSISTENT_SAMREPL |
+ NETLOGON_NEG_ARCFOUR |
+ NETLOGON_NEG_PROMOTION_COUNT |
+ NETLOGON_NEG_CHANGELOG_BDC |
+ NETLOGON_NEG_FULL_SYNC_REPL |
+ NETLOGON_NEG_MULTIPLE_SIDS |
+ NETLOGON_NEG_REDO |
+ NETLOGON_NEG_PASSWORD_CHANGE_REFUSAL |
+ NETLOGON_NEG_SEND_PASSWORD_INFO_PDC |
+ NETLOGON_NEG_GENERIC_PASSTHROUGH |
+ NETLOGON_NEG_CONCURRENT_RPC |
+ NETLOGON_NEG_AVOID_ACCOUNT_DB_REPL |
+ NETLOGON_NEG_AVOID_SECURITYAUTH_DB_REPL |
+ NETLOGON_NEG_128BIT |
+ NETLOGON_NEG_TRANSITIVE_TRUSTS |
+ NETLOGON_NEG_DNS_DOMAIN_TRUSTS |
+ NETLOGON_NEG_PASSWORD_SET2 |
+ NETLOGON_NEG_GETDOMAININFO |
+ NETLOGON_NEG_CROSS_FOREST_TRUSTS |
+ NETLOGON_NEG_AUTHENTICATED_RPC_LSASS |
+ NETLOGON_NEG_SCHANNEL)
+*/
+
+#define NETLOGON_NEG_AUTH2_ADS_FLAGS (0x200fbffb | NETLOGON_NEG_ARCFOUR | NETLOGON_NEG_128BIT | NETLOGON_NEG_SCHANNEL)
+
diff --git a/libcli/auth/libcli_auth.h b/libcli/auth/libcli_auth.h
new file mode 100644
index 0000000..c5c7a7b
--- /dev/null
+++ b/libcli/auth/libcli_auth.h
@@ -0,0 +1,28 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __LIBCLI_AUTH_H__
+#define __LIBCLI_AUTH_H__
+
+#include "librpc/gen_ndr/netlogon.h"
+#include "librpc/gen_ndr/wkssvc.h"
+#include "librpc/gen_ndr/schannel.h"
+#include "libcli/auth/credentials.h"
+#include "libcli/auth/ntlm_check.h"
+#include "libcli/auth/proto.h"
+#include "libcli/auth/msrpc_parse.h"
+
+#endif /* __LIBCLI_AUTH_H__ */
diff --git a/libcli/auth/msrpc_parse.c b/libcli/auth/msrpc_parse.c
new file mode 100644
index 0000000..86ba2ec
--- /dev/null
+++ b/libcli/auth/msrpc_parse.c
@@ -0,0 +1,409 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5/SPNEGO routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+ Copyright (C) Andrew Bartlett 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/auth/msrpc_parse.h"
+
+/*
+ this is a tiny msrpc packet generator. I am only using this to
+ avoid tying this code to a particular varient of our rpc code. This
+ generator is not general enough for all our rpc needs, its just
+ enough for the spnego/ntlmssp code
+
+ format specifiers are:
+
+ U = unicode string (input is unix string)
+ a = address (input is char *unix_string)
+ (1 byte type, 1 byte length, unicode/ASCII string, all inline)
+ A = ASCII string (input is unix string)
+ B = data blob (pointer + length)
+ b = data blob in header (pointer + length)
+ D
+ d = word (4 bytes)
+ C = constant ascii string
+ */
+NTSTATUS msrpc_gen(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ const char *format, ...)
+{
+ int i, j;
+ bool ret;
+ va_list ap;
+ char *s;
+ uint8_t *b;
+ int head_size=0, data_size=0;
+ int head_ofs, data_ofs;
+ int *intargs;
+ size_t n;
+
+ DATA_BLOB *pointers;
+
+ pointers = talloc_array(mem_ctx, DATA_BLOB, strlen(format));
+ if (!pointers) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ intargs = talloc_array(pointers, int, strlen(format));
+ if (!intargs) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* first scan the format to work out the header and body size */
+ va_start(ap, format);
+ for (i=0; format[i]; i++) {
+ switch (format[i]) {
+ case 'U':
+ s = va_arg(ap, char *);
+ head_size += 8;
+ ret = push_ucs2_talloc(
+ pointers,
+ (smb_ucs2_t **)(void *)&pointers[i].data,
+ s, &n);
+ if (!ret) {
+ va_end(ap);
+ return map_nt_error_from_unix_common(errno);
+ }
+ pointers[i].length = n;
+ pointers[i].length -= 2;
+ data_size += pointers[i].length;
+ break;
+ case 'A':
+ s = va_arg(ap, char *);
+ head_size += 8;
+ ret = push_ascii_talloc(
+ pointers, (char **)(void *)&pointers[i].data,
+ s, &n);
+ if (!ret) {
+ va_end(ap);
+ return map_nt_error_from_unix_common(errno);
+ }
+ pointers[i].length = n;
+ pointers[i].length -= 1;
+ data_size += pointers[i].length;
+ break;
+ case 'a':
+ j = va_arg(ap, int);
+ intargs[i] = j;
+ s = va_arg(ap, char *);
+ ret = push_ucs2_talloc(
+ pointers,
+ (smb_ucs2_t **)(void *)&pointers[i].data,
+ s, &n);
+ if (!ret) {
+ va_end(ap);
+ return map_nt_error_from_unix_common(errno);
+ }
+ pointers[i].length = n;
+ pointers[i].length -= 2;
+ data_size += pointers[i].length + 4;
+ break;
+ case 'B':
+ b = va_arg(ap, uint8_t *);
+ head_size += 8;
+ pointers[i].data = b;
+ pointers[i].length = va_arg(ap, int);
+ data_size += pointers[i].length;
+ break;
+ case 'b':
+ b = va_arg(ap, uint8_t *);
+ pointers[i].data = b;
+ pointers[i].length = va_arg(ap, int);
+ head_size += pointers[i].length;
+ break;
+ case 'd':
+ j = va_arg(ap, int);
+ intargs[i] = j;
+ head_size += 4;
+ break;
+ case 'C':
+ s = va_arg(ap, char *);
+ pointers[i].data = (uint8_t *)s;
+ pointers[i].length = strlen(s)+1;
+ head_size += pointers[i].length;
+ break;
+ default:
+ va_end(ap);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ va_end(ap);
+
+ if (head_size + data_size == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* allocate the space, then scan the format again to fill in the values */
+ *blob = data_blob_talloc(mem_ctx, NULL, head_size + data_size);
+ if (!blob->data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ head_ofs = 0;
+ data_ofs = head_size;
+
+ va_start(ap, format);
+ for (i=0; format[i]; i++) {
+ switch (format[i]) {
+ case 'U':
+ case 'A':
+ case 'B':
+ n = pointers[i].length;
+ SSVAL(blob->data, head_ofs, n); head_ofs += 2;
+ SSVAL(blob->data, head_ofs, n); head_ofs += 2;
+ SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
+ if (pointers[i].data && n) /* don't follow null pointers... */
+ memcpy(blob->data+data_ofs, pointers[i].data, n);
+ data_ofs += n;
+ break;
+ case 'a':
+ j = intargs[i];
+ SSVAL(blob->data, data_ofs, j); data_ofs += 2;
+
+ n = pointers[i].length;
+ SSVAL(blob->data, data_ofs, n); data_ofs += 2;
+ memcpy(blob->data+data_ofs, pointers[i].data, n);
+ data_ofs += n;
+ break;
+ case 'd':
+ j = intargs[i];
+ SIVAL(blob->data, head_ofs, j);
+ head_ofs += 4;
+ break;
+ case 'b':
+ n = pointers[i].length;
+ if (pointers[i].data && n) {
+ /* don't follow null pointers... */
+ memcpy(blob->data + head_ofs, pointers[i].data, n);
+ }
+ head_ofs += n;
+ break;
+ case 'C':
+ n = pointers[i].length;
+ memcpy(blob->data + head_ofs, pointers[i].data, n);
+ head_ofs += n;
+ break;
+ default:
+ va_end(ap);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ va_end(ap);
+
+ talloc_free(pointers);
+
+ return NT_STATUS_OK;
+}
+
+
+/* a helpful macro to avoid running over the end of our blob */
+#define NEED_DATA(amount) \
+if ((head_ofs + amount) > blob->length) { \
+ va_end(ap); \
+ return false; \
+}
+
+/**
+ this is a tiny msrpc packet parser. This the the partner of msrpc_gen
+
+ format specifiers are:
+
+ U = unicode string (output is unix string)
+ A = ascii string
+ B = data blob
+ b = data blob in header
+ d = word (4 bytes)
+ C = constant ascii string
+ */
+
+bool msrpc_parse(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *blob,
+ const char *format, ...)
+{
+ int i;
+ va_list ap;
+ char **ps, *s;
+ DATA_BLOB *b;
+ size_t head_ofs = 0;
+ uint16_t len1, len2;
+ uint32_t ptr;
+ uint32_t *v;
+ bool ret = true;
+
+ va_start(ap, format);
+ for (i=0; format[i]; i++) {
+ switch (format[i]) {
+ case 'U':
+ NEED_DATA(8);
+ len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
+
+ ps = va_arg(ap, char **);
+ if (len1 == 0 && len2 == 0) {
+ *ps = talloc_strdup(mem_ctx, "");
+ if (*ps == NULL) {
+ ret = false;
+ goto cleanup;
+ }
+ } else {
+ /* make sure its in the right format - be strict */
+ if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
+ ret = false;
+ goto cleanup;
+ }
+ if (len1 & 1) {
+ /* if odd length and unicode */
+ ret = false;
+ goto cleanup;
+ }
+ if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
+ blob->data + ptr < blob->data) {
+ ret = false;
+ goto cleanup;
+ }
+
+ if (0 < len1) {
+ size_t pull_len;
+ if (!convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX,
+ blob->data + ptr, len1,
+ ps, &pull_len)) {
+ ret = false;
+ goto cleanup;
+ }
+ } else {
+ *ps = talloc_strdup(mem_ctx, "");
+ if (*ps == NULL) {
+ ret = false;
+ goto cleanup;
+ }
+ }
+ }
+ break;
+ case 'A':
+ NEED_DATA(8);
+ len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
+
+ ps = (char **)va_arg(ap, char **);
+ /* make sure its in the right format - be strict */
+ if (len1 == 0 && len2 == 0) {
+ *ps = talloc_strdup(mem_ctx, "");
+ if (*ps == NULL) {
+ ret = false;
+ goto cleanup;
+ }
+ } else {
+ if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
+ ret = false;
+ goto cleanup;
+ }
+
+ if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
+ blob->data + ptr < blob->data) {
+ ret = false;
+ goto cleanup;
+ }
+
+ if (0 < len1) {
+ size_t pull_len;
+
+ if (!convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX,
+ blob->data + ptr, len1,
+ ps, &pull_len)) {
+ ret = false;
+ goto cleanup;
+ }
+ } else {
+ *ps = talloc_strdup(mem_ctx, "");
+ if (*ps == NULL) {
+ ret = false;
+ goto cleanup;
+ }
+ }
+ }
+ break;
+ case 'B':
+ NEED_DATA(8);
+ len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
+
+ b = (DATA_BLOB *)va_arg(ap, void *);
+ if (len1 == 0 && len2 == 0) {
+ *b = data_blob_talloc(mem_ctx, NULL, 0);
+ } else {
+ /* make sure its in the right format - be strict */
+ if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
+ ret = false;
+ goto cleanup;
+ }
+
+ if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
+ blob->data + ptr < blob->data) {
+ ret = false;
+ goto cleanup;
+ }
+
+ *b = data_blob_talloc(mem_ctx, blob->data + ptr, len1);
+ }
+ break;
+ case 'b':
+ b = (DATA_BLOB *)va_arg(ap, void *);
+ len1 = va_arg(ap, unsigned int);
+ /* make sure its in the right format - be strict */
+ NEED_DATA(len1);
+ if (blob->data + head_ofs < (uint8_t *)head_ofs ||
+ blob->data + head_ofs < blob->data) {
+ ret = false;
+ goto cleanup;
+ }
+
+ *b = data_blob_talloc(mem_ctx, blob->data + head_ofs, len1);
+ head_ofs += len1;
+ break;
+ case 'd':
+ v = va_arg(ap, uint32_t *);
+ NEED_DATA(4);
+ *v = IVAL(blob->data, head_ofs); head_ofs += 4;
+ break;
+ case 'C':
+ s = va_arg(ap, char *);
+
+ if (blob->data + head_ofs < (uint8_t *)head_ofs ||
+ blob->data + head_ofs < blob->data ||
+ (head_ofs + (strlen(s) + 1)) > blob->length) {
+ ret = false;
+ goto cleanup;
+ }
+
+ if (memcmp(blob->data + head_ofs, s, strlen(s)+1) != 0) {
+ ret = false;
+ goto cleanup;
+ }
+ head_ofs += (strlen(s) + 1);
+
+ break;
+ }
+ }
+
+cleanup:
+ va_end(ap);
+ return ret;
+}
diff --git a/libcli/auth/msrpc_parse.h b/libcli/auth/msrpc_parse.h
new file mode 100644
index 0000000..47529f2
--- /dev/null
+++ b/libcli/auth/msrpc_parse.h
@@ -0,0 +1,58 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5/SPNEGO routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+ Copyright (C) Andrew Bartlett 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBCLI_AUTH_MSRPC_PARSE_H__
+#define _LIBCLI_AUTH_MSRPC_PARSE_H__
+
+#undef _PRINTF_ATTRIBUTE
+#define _PRINTF_ATTRIBUTE(a1, a2) PRINTF_ATTRIBUTE(a1, a2)
+
+/* this file contains prototypes for functions that are private
+ * to this subsystem or library. These functions should not be
+ * used outside this particular subsystem! */
+
+
+/* The following definitions come from /home/jeremy/src/samba/git/master/source3/../source4/../libcli/auth/msrpc_parse.c */
+
+NTSTATUS msrpc_gen(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ const char *format, ...);
+
+/**
+ this is a tiny msrpc packet parser. This the the partner of msrpc_gen
+
+ format specifiers are:
+
+ U = unicode string (output is unix string)
+ A = ascii string
+ B = data blob
+ b = data blob in header
+ d = word (4 bytes)
+ C = constant ascii string
+ */
+bool msrpc_parse(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *blob,
+ const char *format, ...);
+#undef _PRINTF_ATTRIBUTE
+#define _PRINTF_ATTRIBUTE(a1, a2)
+
+#endif
+
diff --git a/libcli/auth/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c
new file mode 100644
index 0000000..fb53ee5
--- /dev/null
+++ b/libcli/auth/netlogon_creds_cli.c
@@ -0,0 +1,4254 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ module to store/fetch session keys for the schannel client
+
+ Copyright (C) Stefan Metzmacher 2013
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "lib/util/util_tdb.h"
+#include "libcli/security/security.h"
+#include "../lib/param/param.h"
+#include "../libcli/auth/schannel.h"
+#include "../librpc/gen_ndr/ndr_schannel.h"
+#include "../librpc/gen_ndr/ndr_netlogon_c.h"
+#include "../librpc/gen_ndr/ndr_netlogon.h"
+#include "../librpc/gen_ndr/server_id.h"
+#include "netlogon_creds_cli.h"
+#include "source3/include/messages.h"
+#include "source3/include/g_lock.h"
+#include "libds/common/roles.h"
+#include "lib/crypto/md4.h"
+#include "auth/credentials/credentials.h"
+#include "lib/param/loadparm.h"
+
+struct netlogon_creds_cli_locked_state;
+
+struct netlogon_creds_cli_context {
+ struct {
+ const char *computer;
+ const char *account;
+ uint32_t proposed_flags;
+ uint32_t required_flags;
+ enum netr_SchannelType type;
+ enum dcerpc_AuthLevel auth_level;
+ } client;
+
+ struct {
+ const char *computer;
+ const char *netbios_domain;
+ const char *dns_domain;
+ uint32_t cached_flags;
+ bool try_validation6;
+ bool try_logon_ex;
+ bool try_logon_with;
+ } server;
+
+ struct {
+ const char *key_name;
+ TDB_DATA key_data;
+ struct db_context *ctx;
+ struct g_lock_ctx *g_ctx;
+ struct netlogon_creds_cli_locked_state *locked_state;
+ enum netlogon_creds_cli_lck_type lock;
+ } db;
+};
+
+struct netlogon_creds_cli_locked_state {
+ struct netlogon_creds_cli_context *context;
+ bool is_glocked;
+ struct netlogon_creds_CredentialState *creds;
+};
+
+static int netlogon_creds_cli_locked_state_destructor(
+ struct netlogon_creds_cli_locked_state *state)
+{
+ struct netlogon_creds_cli_context *context = state->context;
+
+ if (context == NULL) {
+ return 0;
+ }
+
+ if (context->db.locked_state == state) {
+ context->db.locked_state = NULL;
+ }
+
+ if (state->is_glocked) {
+ g_lock_unlock(context->db.g_ctx,
+ string_term_tdb_data(context->db.key_name));
+ }
+
+ return 0;
+}
+
+static NTSTATUS netlogon_creds_cli_context_common(
+ const char *client_computer,
+ const char *client_account,
+ enum netr_SchannelType type,
+ enum dcerpc_AuthLevel auth_level,
+ uint32_t proposed_flags,
+ uint32_t required_flags,
+ const char *server_computer,
+ const char *server_netbios_domain,
+ const char *server_dns_domain,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_context **_context)
+{
+ struct netlogon_creds_cli_context *context = NULL;
+ char *_key_name = NULL;
+ size_t server_netbios_name_len;
+ char *p = NULL;
+
+ *_context = NULL;
+
+ context = talloc_zero(mem_ctx, struct netlogon_creds_cli_context);
+ if (context == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->client.computer = talloc_strdup(context, client_computer);
+ if (context->client.computer == NULL) {
+ TALLOC_FREE(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->client.account = talloc_strdup(context, client_account);
+ if (context->client.account == NULL) {
+ TALLOC_FREE(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->client.proposed_flags = proposed_flags;
+ context->client.required_flags = required_flags;
+ context->client.type = type;
+ context->client.auth_level = auth_level;
+
+ context->server.computer = talloc_strdup(context, server_computer);
+ if (context->server.computer == NULL) {
+ TALLOC_FREE(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->server.netbios_domain = talloc_strdup(context, server_netbios_domain);
+ if (context->server.netbios_domain == NULL) {
+ TALLOC_FREE(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->server.dns_domain = talloc_strdup(context, server_dns_domain);
+ if (context->server.dns_domain == NULL) {
+ TALLOC_FREE(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * TODO:
+ * Force the callers to provide a unique
+ * value for server_computer and use this directly.
+ *
+ * For now we have to deal with
+ * "HOSTNAME" vs. "hostname.example.com".
+ */
+
+ p = strchr(server_computer, '.');
+ if (p != NULL) {
+ server_netbios_name_len = p-server_computer;
+ } else {
+ server_netbios_name_len = strlen(server_computer);
+ }
+
+ _key_name = talloc_asprintf(context, "CLI[%s/%s]/SRV[%.*s/%s]",
+ client_computer,
+ client_account,
+ (int)server_netbios_name_len,
+ server_computer,
+ server_netbios_domain);
+ if (_key_name == NULL) {
+ TALLOC_FREE(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->db.key_name = talloc_strdup_upper(context, _key_name);
+ TALLOC_FREE(_key_name);
+ if (context->db.key_name == NULL) {
+ TALLOC_FREE(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->db.key_data = string_term_tdb_data(context->db.key_name);
+
+ *_context = context;
+ return NT_STATUS_OK;
+}
+
+static struct db_context *netlogon_creds_cli_global_db;
+
+NTSTATUS netlogon_creds_cli_set_global_db(struct loadparm_context *lp_ctx,
+ struct db_context **db)
+{
+ netlogon_creds_cli_warn_options(lp_ctx);
+
+ if (netlogon_creds_cli_global_db != NULL) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ netlogon_creds_cli_global_db = talloc_move(NULL, db);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_open_global_db(struct loadparm_context *lp_ctx)
+{
+ char *fname;
+ struct db_context *global_db;
+ int hash_size, tdb_flags;
+
+ netlogon_creds_cli_warn_options(lp_ctx);
+
+ if (netlogon_creds_cli_global_db != NULL) {
+ return NT_STATUS_OK;
+ }
+
+ fname = lpcfg_private_db_path(NULL, lp_ctx, "netlogon_creds_cli");
+ if (fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ hash_size = lpcfg_tdb_hash_size(lp_ctx, fname);
+ tdb_flags = lpcfg_tdb_flags(
+ lp_ctx,
+ TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH);
+
+ global_db = dbwrap_local_open(
+ NULL,
+ fname,
+ hash_size,
+ tdb_flags,
+ O_RDWR|O_CREAT,
+ 0600,
+ DBWRAP_LOCK_ORDER_2,
+ DBWRAP_FLAG_NONE);
+ if (global_db == NULL) {
+ DEBUG(0,("netlogon_creds_cli_open_global_db: Failed to open %s - %s\n",
+ fname, strerror(errno)));
+ talloc_free(fname);
+ return NT_STATUS_NO_MEMORY;
+ }
+ TALLOC_FREE(fname);
+
+ netlogon_creds_cli_global_db = global_db;
+ return NT_STATUS_OK;
+}
+
+void netlogon_creds_cli_close_global_db(void)
+{
+ TALLOC_FREE(netlogon_creds_cli_global_db);
+}
+
+void netlogon_creds_cli_warn_options(struct loadparm_context *lp_ctx)
+{
+ bool global_reject_md5_servers = lpcfg_reject_md5_servers(lp_ctx);
+ bool global_require_strong_key = lpcfg_require_strong_key(lp_ctx);
+ int global_client_schannel = lpcfg_client_schannel(lp_ctx);
+ bool global_seal_secure_channel = lpcfg_winbind_sealed_pipes(lp_ctx);
+ int global_kerberos_enctypes = lpcfg_kerberos_encryption_types(lp_ctx);
+ static bool warned_global_reject_md5_servers = false;
+ static bool warned_global_require_strong_key = false;
+ static bool warned_global_client_schannel = false;
+ static bool warned_global_seal_secure_channel = false;
+ static bool warned_global_kerberos_encryption_types = false;
+ static int warned_global_pid = 0;
+ int current_pid = tevent_cached_getpid();
+
+ if (warned_global_pid != current_pid) {
+ warned_global_reject_md5_servers = false;
+ warned_global_require_strong_key = false;
+ warned_global_client_schannel = false;
+ warned_global_seal_secure_channel = false;
+ warned_global_kerberos_encryption_types = false;
+ warned_global_pid = current_pid;
+ }
+
+ if (!global_reject_md5_servers && !warned_global_reject_md5_servers) {
+ /*
+ * We want admins to notice their misconfiguration!
+ */
+ DBG_ERR("CVE-2022-38023 (and others): "
+ "Please configure 'reject md5 servers = yes' (the default), "
+ "See https://bugzilla.samba.org/show_bug.cgi?id=15240\n");
+ warned_global_reject_md5_servers = true;
+ }
+
+ if (!global_require_strong_key && !warned_global_require_strong_key) {
+ /*
+ * We want admins to notice their misconfiguration!
+ */
+ DBG_ERR("CVE-2022-38023 (and others): "
+ "Please configure 'require strong key = yes' (the default), "
+ "See https://bugzilla.samba.org/show_bug.cgi?id=15240\n");
+ warned_global_require_strong_key = true;
+ }
+
+ if (global_client_schannel != true && !warned_global_client_schannel) {
+ /*
+ * We want admins to notice their misconfiguration!
+ */
+ DBG_ERR("CVE-2022-38023 (and others): "
+ "Please configure 'client schannel = yes' (the default), "
+ "See https://bugzilla.samba.org/show_bug.cgi?id=15240\n");
+ warned_global_client_schannel = true;
+ }
+
+ if (!global_seal_secure_channel && !warned_global_seal_secure_channel) {
+ /*
+ * We want admins to notice their misconfiguration!
+ */
+ DBG_ERR("CVE-2022-38023 (and others): "
+ "Please configure 'winbind sealed pipes = yes' (the default), "
+ "See https://bugzilla.samba.org/show_bug.cgi?id=15240\n");
+ warned_global_seal_secure_channel = true;
+ }
+
+ if (global_kerberos_enctypes == KERBEROS_ETYPES_LEGACY &&
+ !warned_global_kerberos_encryption_types)
+ {
+ /*
+ * We want admins to notice their misconfiguration!
+ */
+ DBG_ERR("CVE-2022-37966: "
+ "Please void 'kerberos encryption types = legacy', "
+ "See https://bugzilla.samba.org/show_bug.cgi?id=15237\n");
+ warned_global_kerberos_encryption_types = true;
+ }
+}
+
+NTSTATUS netlogon_creds_cli_context_global(struct loadparm_context *lp_ctx,
+ struct messaging_context *msg_ctx,
+ const char *client_account,
+ enum netr_SchannelType type,
+ const char *server_computer,
+ const char *server_netbios_domain,
+ const char *server_dns_domain,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_context **_context)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+ struct netlogon_creds_cli_context *context = NULL;
+ const char *client_computer;
+ uint32_t proposed_flags;
+ uint32_t required_flags = 0;
+ bool reject_md5_servers = true;
+ bool require_strong_key = true;
+ int require_sign_or_seal = true;
+ bool seal_secure_channel = true;
+ enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
+ bool neutralize_nt4_emulation = false;
+
+ *_context = NULL;
+
+ if (msg_ctx == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ client_computer = lpcfg_netbios_name(lp_ctx);
+ if (strlen(client_computer) > 15) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /*
+ * allow overwrite per domain
+ * reject md5 servers:<netbios_domain>
+ */
+ reject_md5_servers = lpcfg_reject_md5_servers(lp_ctx);
+ reject_md5_servers = lpcfg_parm_bool(lp_ctx, NULL,
+ "reject md5 servers",
+ server_netbios_domain,
+ reject_md5_servers);
+
+ /*
+ * allow overwrite per domain
+ * require strong key:<netbios_domain>
+ */
+ require_strong_key = lpcfg_require_strong_key(lp_ctx);
+ require_strong_key = lpcfg_parm_bool(lp_ctx, NULL,
+ "require strong key",
+ server_netbios_domain,
+ require_strong_key);
+
+ /*
+ * allow overwrite per domain
+ * client schannel:<netbios_domain>
+ */
+ require_sign_or_seal = lpcfg_client_schannel(lp_ctx);
+ require_sign_or_seal = lpcfg_parm_int(lp_ctx, NULL,
+ "client schannel",
+ server_netbios_domain,
+ require_sign_or_seal);
+
+ /*
+ * allow overwrite per domain
+ * winbind sealed pipes:<netbios_domain>
+ */
+ seal_secure_channel = lpcfg_winbind_sealed_pipes(lp_ctx);
+ seal_secure_channel = lpcfg_parm_bool(lp_ctx, NULL,
+ "winbind sealed pipes",
+ server_netbios_domain,
+ seal_secure_channel);
+
+ /*
+ * allow overwrite per domain
+ * neutralize nt4 emulation:<netbios_domain>
+ */
+ neutralize_nt4_emulation = lpcfg_neutralize_nt4_emulation(lp_ctx);
+ neutralize_nt4_emulation = lpcfg_parm_bool(lp_ctx, NULL,
+ "neutralize nt4 emulation",
+ server_netbios_domain,
+ neutralize_nt4_emulation);
+
+ proposed_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+ proposed_flags |= NETLOGON_NEG_SUPPORTS_AES;
+
+ switch (type) {
+ case SEC_CHAN_WKSTA:
+ if (lpcfg_security(lp_ctx) == SEC_ADS) {
+ /*
+ * AD domains should be secure
+ */
+ required_flags |= NETLOGON_NEG_PASSWORD_SET2;
+ require_sign_or_seal = true;
+ require_strong_key = true;
+ }
+ break;
+
+ case SEC_CHAN_DOMAIN:
+ break;
+
+ case SEC_CHAN_DNS_DOMAIN:
+ /*
+ * AD domains should be secure
+ */
+ required_flags |= NETLOGON_NEG_PASSWORD_SET2;
+ require_sign_or_seal = true;
+ require_strong_key = true;
+ neutralize_nt4_emulation = true;
+ break;
+
+ case SEC_CHAN_BDC:
+ required_flags |= NETLOGON_NEG_PASSWORD_SET2;
+ require_sign_or_seal = true;
+ require_strong_key = true;
+ break;
+
+ case SEC_CHAN_RODC:
+ required_flags |= NETLOGON_NEG_RODC_PASSTHROUGH;
+ required_flags |= NETLOGON_NEG_PASSWORD_SET2;
+ require_sign_or_seal = true;
+ require_strong_key = true;
+ neutralize_nt4_emulation = true;
+ break;
+
+ default:
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (neutralize_nt4_emulation) {
+ proposed_flags |= NETLOGON_NEG_NEUTRALIZE_NT4_EMULATION;
+ }
+
+ if (require_sign_or_seal) {
+ required_flags |= NETLOGON_NEG_ARCFOUR;
+ required_flags |= NETLOGON_NEG_AUTHENTICATED_RPC;
+ } else {
+ proposed_flags &= ~NETLOGON_NEG_AUTHENTICATED_RPC;
+ }
+
+ if (reject_md5_servers) {
+ required_flags |= NETLOGON_NEG_ARCFOUR;
+ required_flags |= NETLOGON_NEG_PASSWORD_SET2;
+ required_flags |= NETLOGON_NEG_SUPPORTS_AES;
+ required_flags |= NETLOGON_NEG_AUTHENTICATED_RPC;
+ }
+
+ if (require_strong_key) {
+ required_flags |= NETLOGON_NEG_ARCFOUR;
+ required_flags |= NETLOGON_NEG_STRONG_KEYS;
+ required_flags |= NETLOGON_NEG_AUTHENTICATED_RPC;
+ }
+
+ /*
+ * If weak crypto is disabled, do not announce that we support RC4 and
+ * require AES.
+ */
+ if (lpcfg_weak_crypto(lp_ctx) == SAMBA_WEAK_CRYPTO_DISALLOWED) {
+ required_flags &= ~NETLOGON_NEG_ARCFOUR;
+ required_flags |= NETLOGON_NEG_SUPPORTS_AES;
+ proposed_flags &= ~NETLOGON_NEG_ARCFOUR;
+ proposed_flags |= NETLOGON_NEG_SUPPORTS_AES;
+ }
+
+ proposed_flags |= required_flags;
+
+ if (seal_secure_channel) {
+ auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+ } else {
+ auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
+ }
+
+ status = netlogon_creds_cli_context_common(client_computer,
+ client_account,
+ type,
+ auth_level,
+ proposed_flags,
+ required_flags,
+ server_computer,
+ server_netbios_domain,
+ "",
+ mem_ctx,
+ &context);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ context->db.g_ctx = g_lock_ctx_init(context, msg_ctx);
+ if (context->db.g_ctx == NULL) {
+ TALLOC_FREE(context);
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = netlogon_creds_cli_open_global_db(lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(context);
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->db.ctx = netlogon_creds_cli_global_db;
+ *_context = context;
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_bind_cli_credentials(
+ struct netlogon_creds_cli_context *context, TALLOC_CTX *mem_ctx,
+ struct cli_credentials **pcli_creds)
+{
+ struct cli_credentials *cli_creds;
+ struct netlogon_creds_CredentialState *ncreds;
+ NTSTATUS status;
+
+ cli_creds = cli_credentials_init(mem_ctx);
+ if (cli_creds == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ cli_credentials_set_secure_channel_type(cli_creds,
+ context->client.type);
+ cli_credentials_set_username(cli_creds, context->client.account,
+ CRED_SPECIFIED);
+ cli_credentials_set_domain(cli_creds, context->server.netbios_domain,
+ CRED_SPECIFIED);
+ cli_credentials_set_realm(cli_creds, context->server.dns_domain,
+ CRED_SPECIFIED);
+
+ status = netlogon_creds_cli_get(context, cli_creds, &ncreds);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(cli_creds);
+ return status;
+ }
+ cli_credentials_set_netlogon_creds(cli_creds, ncreds);
+
+ *pcli_creds = cli_creds;
+ return NT_STATUS_OK;
+}
+
+char *netlogon_creds_cli_debug_string(
+ const struct netlogon_creds_cli_context *context,
+ TALLOC_CTX *mem_ctx)
+{
+ return talloc_asprintf(mem_ctx, "netlogon_creds_cli:%s",
+ context->db.key_name);
+}
+
+enum dcerpc_AuthLevel netlogon_creds_cli_auth_level(
+ struct netlogon_creds_cli_context *context)
+{
+ return context->client.auth_level;
+}
+
+static bool netlogon_creds_cli_downgraded(uint32_t negotiated_flags,
+ uint32_t proposed_flags,
+ uint32_t required_flags)
+{
+ uint32_t req_flags = required_flags;
+ uint32_t tmp_flags;
+
+ req_flags = required_flags;
+ if ((negotiated_flags & NETLOGON_NEG_SUPPORTS_AES) &&
+ (proposed_flags & NETLOGON_NEG_SUPPORTS_AES))
+ {
+ req_flags &= ~NETLOGON_NEG_ARCFOUR|NETLOGON_NEG_STRONG_KEYS;
+ }
+
+ tmp_flags = negotiated_flags;
+ tmp_flags &= req_flags;
+ if (tmp_flags != req_flags) {
+ return true;
+ }
+
+ return false;
+}
+
+struct netlogon_creds_cli_fetch_state {
+ TALLOC_CTX *mem_ctx;
+ struct netlogon_creds_CredentialState *creds;
+ uint32_t proposed_flags;
+ uint32_t required_flags;
+ NTSTATUS status;
+};
+
+static void netlogon_creds_cli_fetch_parser(TDB_DATA key, TDB_DATA data,
+ void *private_data)
+{
+ struct netlogon_creds_cli_fetch_state *state =
+ (struct netlogon_creds_cli_fetch_state *)private_data;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ bool downgraded;
+
+ state->creds = talloc_zero(state->mem_ctx,
+ struct netlogon_creds_CredentialState);
+ if (state->creds == NULL) {
+ state->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ blob.data = data.dptr;
+ blob.length = data.dsize;
+
+ ndr_err = ndr_pull_struct_blob(&blob, state->creds, state->creds,
+ (ndr_pull_flags_fn_t)ndr_pull_netlogon_creds_CredentialState);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(state->creds);
+ state->status = ndr_map_error2ntstatus(ndr_err);
+ return;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(netlogon_creds_CredentialState, state->creds);
+ }
+
+ downgraded = netlogon_creds_cli_downgraded(
+ state->creds->negotiate_flags,
+ state->proposed_flags,
+ state->required_flags);
+ if (downgraded) {
+ TALLOC_FREE(state->creds);
+ state->status = NT_STATUS_DOWNGRADE_DETECTED;
+ return;
+ }
+
+ state->status = NT_STATUS_OK;
+}
+
+static NTSTATUS netlogon_creds_cli_get_internal(
+ struct netlogon_creds_cli_context *context,
+ TALLOC_CTX *mem_ctx, struct netlogon_creds_CredentialState **pcreds);
+
+NTSTATUS netlogon_creds_cli_get(struct netlogon_creds_cli_context *context,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_CredentialState **_creds)
+{
+ NTSTATUS status;
+ struct netlogon_creds_CredentialState *creds;
+
+ *_creds = NULL;
+
+ status = netlogon_creds_cli_get_internal(context, mem_ctx, &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * mark it as invalid for step operations.
+ */
+ creds->sequence = 0;
+ creds->seed = (struct netr_Credential) {{0}};
+ creds->client = (struct netr_Credential) {{0}};
+ creds->server = (struct netr_Credential) {{0}};
+
+ *_creds = creds;
+ return NT_STATUS_OK;
+}
+
+bool netlogon_creds_cli_validate(struct netlogon_creds_cli_context *context,
+ const struct netlogon_creds_CredentialState *creds1)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct netlogon_creds_CredentialState *creds2;
+ DATA_BLOB blob1;
+ DATA_BLOB blob2;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ bool equal;
+
+ status = netlogon_creds_cli_get(context, frame, &creds2);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob1, frame, creds1,
+ (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob2, frame, creds2,
+ (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ equal = data_blob_equal_const_time(&blob1, &blob2);
+
+ TALLOC_FREE(frame);
+
+ return equal;
+}
+
+static NTSTATUS netlogon_creds_cli_store_internal(
+ struct netlogon_creds_cli_context *context,
+ struct netlogon_creds_CredentialState *creds)
+{
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ TDB_DATA data;
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, creds, creds,
+ (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ return status;
+ }
+
+ data.dptr = blob.data;
+ data.dsize = blob.length;
+
+ status = dbwrap_store(context->db.ctx,
+ context->db.key_data,
+ data, TDB_REPLACE);
+ TALLOC_FREE(data.dptr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_store(struct netlogon_creds_cli_context *context,
+ struct netlogon_creds_CredentialState *creds)
+{
+ NTSTATUS status;
+
+ if (context->db.locked_state == NULL) {
+ /*
+ * this was not the result of netlogon_creds_cli_lock*()
+ */
+ return NT_STATUS_INVALID_PAGE_PROTECTION;
+ }
+
+ if (context->db.locked_state->creds != creds) {
+ /*
+ * this was not the result of netlogon_creds_cli_lock*()
+ */
+ return NT_STATUS_INVALID_PAGE_PROTECTION;
+ }
+
+ status = netlogon_creds_cli_store_internal(context, creds);
+ return status;
+}
+
+static NTSTATUS netlogon_creds_cli_delete_internal(
+ struct netlogon_creds_cli_context *context)
+{
+ NTSTATUS status;
+ status = dbwrap_delete(context->db.ctx, context->db.key_data);
+ return status;
+}
+
+NTSTATUS netlogon_creds_cli_delete_lck(
+ struct netlogon_creds_cli_context *context)
+{
+ NTSTATUS status;
+
+ if (context->db.lock != NETLOGON_CREDS_CLI_LCK_EXCLUSIVE) {
+ return NT_STATUS_NOT_LOCKED;
+ }
+
+ status = netlogon_creds_cli_delete_internal(context);
+ return status;
+}
+
+NTSTATUS netlogon_creds_cli_delete(struct netlogon_creds_cli_context *context,
+ struct netlogon_creds_CredentialState *creds)
+{
+ NTSTATUS status;
+
+ if (context->db.locked_state == NULL) {
+ /*
+ * this was not the result of netlogon_creds_cli_lock*()
+ */
+ return NT_STATUS_INVALID_PAGE_PROTECTION;
+ }
+
+ if (context->db.locked_state->creds != creds) {
+ /*
+ * this was not the result of netlogon_creds_cli_lock*()
+ */
+ return NT_STATUS_INVALID_PAGE_PROTECTION;
+ }
+
+ status = netlogon_creds_cli_delete_internal(context);
+ return status;
+}
+
+struct netlogon_creds_cli_lock_state {
+ struct netlogon_creds_cli_locked_state *locked_state;
+ struct netlogon_creds_CredentialState *creds;
+};
+
+static void netlogon_creds_cli_lock_done(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_lock_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_lock_state *state;
+ struct netlogon_creds_cli_locked_state *locked_state;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_lock_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (context->db.locked_state != NULL) {
+ tevent_req_nterror(req, NT_STATUS_LOCK_NOT_GRANTED);
+ return tevent_req_post(req, ev);
+ }
+
+ locked_state = talloc_zero(state, struct netlogon_creds_cli_locked_state);
+ if (tevent_req_nomem(locked_state, req)) {
+ return tevent_req_post(req, ev);
+ }
+ talloc_set_destructor(locked_state,
+ netlogon_creds_cli_locked_state_destructor);
+ locked_state->context = context;
+
+ context->db.locked_state = locked_state;
+ state->locked_state = locked_state;
+
+ if (context->db.g_ctx == NULL) {
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_get_internal(
+ context, state, &state->creds);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+ }
+
+ subreq = g_lock_lock_send(state, ev,
+ context->db.g_ctx,
+ string_term_tdb_data(context->db.key_name),
+ G_LOCK_WRITE);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, netlogon_creds_cli_lock_done, req);
+
+ return req;
+}
+
+static void netlogon_creds_cli_lock_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_lock_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_lock_state);
+ NTSTATUS status;
+
+ status = g_lock_lock_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->locked_state->is_glocked = true;
+
+ status = netlogon_creds_cli_get_internal(state->locked_state->context,
+ state, &state->creds);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS netlogon_creds_cli_get_internal(
+ struct netlogon_creds_cli_context *context,
+ TALLOC_CTX *mem_ctx, struct netlogon_creds_CredentialState **pcreds)
+{
+ struct netlogon_creds_cli_fetch_state fstate = {
+ .status = NT_STATUS_INTERNAL_ERROR,
+ .proposed_flags = context->client.proposed_flags,
+ .required_flags = context->client.required_flags,
+ };
+ NTSTATUS status;
+
+ fstate.mem_ctx = mem_ctx;
+ status = dbwrap_parse_record(context->db.ctx,
+ context->db.key_data,
+ netlogon_creds_cli_fetch_parser,
+ &fstate);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(fstate.status)) {
+ return fstate.status;
+ }
+
+ if (context->server.cached_flags == fstate.creds->negotiate_flags) {
+ *pcreds = fstate.creds;
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * It is really important to try SamLogonEx here,
+ * because multiple processes can talk to the same
+ * domain controller, without using the credential
+ * chain.
+ *
+ * With a normal SamLogon call, we must keep the
+ * credentials chain updated and intact between all
+ * users of the machine account (which would imply
+ * cross-node communication for every NTLM logon).
+ *
+ * The credentials chain is not per NETLOGON pipe
+ * connection, but globally on the server/client pair
+ * by computer name.
+ *
+ * It's also important to use NetlogonValidationSamInfo4 (6),
+ * because it relies on the rpc transport encryption
+ * and avoids using the global netlogon schannel
+ * session key to en/decrypt secret information
+ * like the user_session_key for network logons.
+ *
+ * [MS-APDS] 3.1.5.2 NTLM Network Logon
+ * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
+ * NETLOGON_NEG_AUTHENTICATED_RPC set together
+ * are the indication that the server supports
+ * NetlogonValidationSamInfo4 (6). And it must only
+ * be used if "SealSecureChannel" is used.
+ *
+ * The "SealSecureChannel" AUTH_TYPE_SCHANNEL/AUTH_LEVEL_PRIVACY
+ * check is done in netlogon_creds_cli_LogonSamLogon*().
+ */
+
+ context->server.cached_flags = fstate.creds->negotiate_flags;
+ context->server.try_validation6 = true;
+ context->server.try_logon_ex = true;
+ context->server.try_logon_with = true;
+
+ if (!(context->server.cached_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
+ context->server.try_validation6 = false;
+ context->server.try_logon_ex = false;
+ }
+ if (!(context->server.cached_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
+ context->server.try_validation6 = false;
+ }
+
+ *pcreds = fstate.creds;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_lock_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_CredentialState **creds)
+{
+ struct netlogon_creds_cli_lock_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_lock_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ talloc_steal(state->creds, state->locked_state);
+ state->locked_state->creds = state->creds;
+ *creds = talloc_move(mem_ctx, &state->creds);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_lock(struct netlogon_creds_cli_context *context,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_CredentialState **creds)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = netlogon_creds_cli_lock_send(frame, ev, context);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_lock_recv(req, mem_ctx, creds);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct netlogon_creds_cli_lck {
+ struct netlogon_creds_cli_context *context;
+};
+
+struct netlogon_creds_cli_lck_state {
+ struct netlogon_creds_cli_lck *lck;
+ enum netlogon_creds_cli_lck_type type;
+};
+
+static void netlogon_creds_cli_lck_locked(struct tevent_req *subreq);
+static int netlogon_creds_cli_lck_destructor(
+ struct netlogon_creds_cli_lck *lck);
+
+struct tevent_req *netlogon_creds_cli_lck_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ enum netlogon_creds_cli_lck_type type)
+{
+ struct tevent_req *req, *subreq;
+ struct netlogon_creds_cli_lck_state *state;
+ enum g_lock_type gtype;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_lck_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (context->db.lock != NETLOGON_CREDS_CLI_LCK_NONE) {
+ DBG_DEBUG("context already locked\n");
+ tevent_req_nterror(req, NT_STATUS_INVALID_LOCK_SEQUENCE);
+ return tevent_req_post(req, ev);
+ }
+
+ switch (type) {
+ case NETLOGON_CREDS_CLI_LCK_SHARED:
+ gtype = G_LOCK_READ;
+ break;
+ case NETLOGON_CREDS_CLI_LCK_EXCLUSIVE:
+ gtype = G_LOCK_WRITE;
+ break;
+ default:
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->lck = talloc(state, struct netlogon_creds_cli_lck);
+ if (tevent_req_nomem(state->lck, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->lck->context = context;
+ state->type = type;
+
+ subreq = g_lock_lock_send(state, ev,
+ context->db.g_ctx,
+ string_term_tdb_data(context->db.key_name),
+ gtype);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, netlogon_creds_cli_lck_locked, req);
+
+ return req;
+}
+
+static void netlogon_creds_cli_lck_locked(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct netlogon_creds_cli_lck_state *state = tevent_req_data(
+ req, struct netlogon_creds_cli_lck_state);
+ NTSTATUS status;
+
+ status = g_lock_lock_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->lck->context->db.lock = state->type;
+ talloc_set_destructor(state->lck, netlogon_creds_cli_lck_destructor);
+
+ tevent_req_done(req);
+}
+
+static int netlogon_creds_cli_lck_destructor(
+ struct netlogon_creds_cli_lck *lck)
+{
+ struct netlogon_creds_cli_context *ctx = lck->context;
+ NTSTATUS status;
+
+ status = g_lock_unlock(ctx->db.g_ctx,
+ string_term_tdb_data(ctx->db.key_name));
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("g_lock_unlock failed: %s\n", nt_errstr(status));
+ smb_panic("g_lock_unlock failed");
+ }
+ ctx->db.lock = NETLOGON_CREDS_CLI_LCK_NONE;
+ return 0;
+}
+
+NTSTATUS netlogon_creds_cli_lck_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_lck **lck)
+{
+ struct netlogon_creds_cli_lck_state *state = tevent_req_data(
+ req, struct netlogon_creds_cli_lck_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *lck = talloc_move(mem_ctx, &state->lck);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_lck(
+ struct netlogon_creds_cli_context *context,
+ enum netlogon_creds_cli_lck_type type,
+ TALLOC_CTX *mem_ctx, struct netlogon_creds_cli_lck **lck)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = netlogon_creds_cli_lck_send(frame, ev, context, type);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_lck_recv(req, mem_ctx, lck);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct netlogon_creds_cli_auth_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+ uint8_t num_nt_hashes;
+ uint8_t idx_nt_hashes;
+ const struct samr_Password * const *nt_hashes;
+ const struct samr_Password *used_nt_hash;
+ char *srv_name_slash;
+ uint32_t current_flags;
+ struct netr_Credential client_challenge;
+ struct netr_Credential server_challenge;
+ struct netlogon_creds_CredentialState *creds;
+ struct netr_Credential client_credential;
+ struct netr_Credential server_credential;
+ uint32_t rid;
+ bool try_auth3;
+ bool try_auth2;
+ bool require_auth2;
+};
+
+static void netlogon_creds_cli_auth_challenge_start(struct tevent_req *req);
+
+struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ uint8_t num_nt_hashes,
+ const struct samr_Password * const *nt_hashes)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_auth_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_auth_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->context = context;
+ state->binding_handle = b;
+ if (num_nt_hashes < 1) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return tevent_req_post(req, ev);
+ }
+ if (num_nt_hashes > 4) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return tevent_req_post(req, ev);
+ }
+
+ state->num_nt_hashes = num_nt_hashes;
+ state->idx_nt_hashes = 0;
+ state->nt_hashes = nt_hashes;
+
+ if (context->db.lock != NETLOGON_CREDS_CLI_LCK_EXCLUSIVE) {
+ tevent_req_nterror(req, NT_STATUS_NOT_LOCKED);
+ return tevent_req_post(req, ev);
+ }
+
+ state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+ context->server.computer);
+ if (tevent_req_nomem(state->srv_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->try_auth3 = true;
+ state->try_auth2 = true;
+
+ if (context->client.required_flags != 0) {
+ state->require_auth2 = true;
+ }
+
+ state->used_nt_hash = state->nt_hashes[state->idx_nt_hashes];
+ state->current_flags = context->client.proposed_flags;
+
+ status = dbwrap_purge(state->context->db.ctx,
+ state->context->db.key_data);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ netlogon_creds_cli_auth_challenge_start(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void netlogon_creds_cli_auth_challenge_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_auth_challenge_start(struct tevent_req *req)
+{
+ struct netlogon_creds_cli_auth_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_auth_state);
+ struct tevent_req *subreq;
+
+ TALLOC_FREE(state->creds);
+
+ netlogon_creds_random_challenge(&state->client_challenge);
+
+ subreq = dcerpc_netr_ServerReqChallenge_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->context->client.computer,
+ &state->client_challenge,
+ &state->server_challenge);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_auth_challenge_done,
+ req);
+}
+
+static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_auth_challenge_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_auth_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_auth_state);
+ NTSTATUS status;
+ NTSTATUS result;
+
+ status = dcerpc_netr_ServerReqChallenge_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (tevent_req_nterror(req, result)) {
+ return;
+ }
+
+ if (!state->try_auth3 && !state->try_auth2) {
+ state->current_flags = 0;
+ }
+
+ /* Calculate the session key and client credentials */
+
+ state->creds = netlogon_creds_client_init(state,
+ state->context->client.account,
+ state->context->client.computer,
+ state->context->client.type,
+ &state->client_challenge,
+ &state->server_challenge,
+ state->used_nt_hash,
+ &state->client_credential,
+ state->current_flags);
+ if (tevent_req_nomem(state->creds, req)) {
+ return;
+ }
+
+ if (state->try_auth3) {
+ subreq = dcerpc_netr_ServerAuthenticate3_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->context->client.account,
+ state->context->client.type,
+ state->context->client.computer,
+ &state->client_credential,
+ &state->server_credential,
+ &state->creds->negotiate_flags,
+ &state->rid);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ } else if (state->try_auth2) {
+ state->rid = 0;
+
+ subreq = dcerpc_netr_ServerAuthenticate2_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->context->client.account,
+ state->context->client.type,
+ state->context->client.computer,
+ &state->client_credential,
+ &state->server_credential,
+ &state->creds->negotiate_flags);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ } else {
+ state->rid = 0;
+
+ subreq = dcerpc_netr_ServerAuthenticate_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->context->client.account,
+ state->context->client.type,
+ state->context->client.computer,
+ &state->client_credential,
+ &state->server_credential);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ }
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_auth_srvauth_done,
+ req);
+}
+
+static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_auth_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_auth_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ TDB_DATA data;
+ bool downgraded;
+
+ if (state->try_auth3) {
+ status = dcerpc_netr_ServerAuthenticate3_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ state->try_auth3 = false;
+ netlogon_creds_cli_auth_challenge_start(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ } else if (state->try_auth2) {
+ status = dcerpc_netr_ServerAuthenticate2_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ state->try_auth2 = false;
+ if (state->require_auth2) {
+ status = NT_STATUS_DOWNGRADE_DETECTED;
+ tevent_req_nterror(req, status);
+ return;
+ }
+ netlogon_creds_cli_auth_challenge_start(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ } else {
+ status = dcerpc_netr_ServerAuthenticate_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED))
+ {
+ tevent_req_nterror(req, result);
+ return;
+ }
+
+ downgraded = netlogon_creds_cli_downgraded(
+ state->creds->negotiate_flags,
+ state->context->client.proposed_flags,
+ state->context->client.required_flags);
+ if (downgraded) {
+ if (NT_STATUS_IS_OK(result)) {
+ tevent_req_nterror(req, NT_STATUS_DOWNGRADE_DETECTED);
+ return;
+ }
+ tevent_req_nterror(req, result);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
+ uint32_t tmp_flags = state->context->client.proposed_flags;
+ if ((state->current_flags == tmp_flags) &&
+ (state->creds->negotiate_flags != tmp_flags))
+ {
+ /*
+ * lets retry with the negotiated flags
+ */
+ state->current_flags = state->creds->negotiate_flags;
+ netlogon_creds_cli_auth_challenge_start(req);
+ return;
+ }
+
+ state->idx_nt_hashes += 1;
+ if (state->idx_nt_hashes >= state->num_nt_hashes) {
+ /*
+ * we already retried, giving up...
+ */
+ tevent_req_nterror(req, result);
+ return;
+ }
+
+ /*
+ * lets retry with the old nt hash.
+ */
+ state->used_nt_hash = state->nt_hashes[state->idx_nt_hashes];
+ state->current_flags = state->context->client.proposed_flags;
+ netlogon_creds_cli_auth_challenge_start(req);
+ return;
+ }
+
+ ok = netlogon_creds_client_check(state->creds,
+ &state->server_credential);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, state, state->creds,
+ (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ data.dptr = blob.data;
+ data.dsize = blob.length;
+
+ status = dbwrap_store(state->context->db.ctx,
+ state->context->db.key_data,
+ data, TDB_REPLACE);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req,
+ uint8_t *idx_nt_hashes)
+{
+ struct netlogon_creds_cli_auth_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_auth_state);
+ NTSTATUS status;
+
+ *idx_nt_hashes = 0;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *idx_nt_hashes = state->idx_nt_hashes;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_auth(struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ uint8_t num_nt_hashes,
+ const struct samr_Password * const *nt_hashes,
+ uint8_t *idx_nt_hashes)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ *idx_nt_hashes = 0;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = netlogon_creds_cli_auth_send(frame, ev, context, b,
+ num_nt_hashes, nt_hashes);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_auth_recv(req, idx_nt_hashes);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct netlogon_creds_cli_check_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+
+ char *srv_name_slash;
+
+ union netr_Capabilities caps;
+
+ struct netlogon_creds_CredentialState *creds;
+ struct netr_Authenticator req_auth;
+ struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_check_cleanup(struct tevent_req *req,
+ NTSTATUS status);
+static void netlogon_creds_cli_check_caps(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_check_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_check_state *state;
+ struct tevent_req *subreq;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_check_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->context = context;
+ state->binding_handle = b;
+
+ if (context->db.lock != NETLOGON_CREDS_CLI_LCK_EXCLUSIVE) {
+ tevent_req_nterror(req, NT_STATUS_NOT_LOCKED);
+ return tevent_req_post(req, ev);
+ }
+
+ status = netlogon_creds_cli_get_internal(context, state,
+ &state->creds);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+ context->server.computer);
+ if (tevent_req_nomem(state->srv_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ dcerpc_binding_handle_auth_info(state->binding_handle,
+ &auth_type, &auth_level);
+
+ if (auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return tevent_req_post(req, ev);
+ }
+
+ switch (auth_level) {
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ break;
+ default:
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * we defer all callbacks in order to cleanup
+ * the database record.
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ status = netlogon_creds_client_authenticator(state->creds,
+ &state->req_auth);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ ZERO_STRUCT(state->rep_auth);
+
+ subreq = dcerpc_netr_LogonGetCapabilities_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->context->client.computer,
+ &state->req_auth,
+ &state->rep_auth,
+ 1,
+ &state->caps);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_check_caps,
+ req);
+
+ return req;
+}
+
+static void netlogon_creds_cli_check_cleanup(struct tevent_req *req,
+ NTSTATUS status)
+{
+ struct netlogon_creds_cli_check_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_check_state);
+
+ if (state->creds == NULL) {
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+ TALLOC_FREE(state->creds);
+ return;
+ }
+
+ netlogon_creds_cli_delete_lck(state->context);
+ TALLOC_FREE(state->creds);
+}
+
+static void netlogon_creds_cli_check_caps(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_check_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_check_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+
+ status = dcerpc_netr_LogonGetCapabilities_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ /*
+ * Note that the negotiated flags are already checked
+ * for our required flags after the ServerAuthenticate3/2 call.
+ */
+ uint32_t negotiated = state->creds->negotiate_flags;
+
+ if (negotiated & NETLOGON_NEG_SUPPORTS_AES) {
+ /*
+ * If we have negotiated NETLOGON_NEG_SUPPORTS_AES
+ * already, we expect this to work!
+ */
+ status = NT_STATUS_DOWNGRADE_DETECTED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+
+ if (negotiated & NETLOGON_NEG_STRONG_KEYS) {
+ /*
+ * If we have negotiated NETLOGON_NEG_STRONG_KEYS
+ * we expect this to work at least as far as the
+ * NOT_SUPPORTED error handled below!
+ *
+ * NT 4.0 and Old Samba servers are not
+ * allowed without "require strong key = no"
+ */
+ status = NT_STATUS_DOWNGRADE_DETECTED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+
+ /*
+ * If we not require NETLOGON_NEG_SUPPORTS_AES or
+ * NETLOGON_NEG_STRONG_KEYS, it's ok to ignore
+ * NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
+ *
+ * This is needed against NT 4.0 and old Samba servers.
+ *
+ * As we're using DCERPC_AUTH_TYPE_SCHANNEL with
+ * DCERPC_AUTH_LEVEL_INTEGRITY or DCERPC_AUTH_LEVEL_PRIVACY
+ * we should detect a faked NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
+ * with the next request as the sequence number processing
+ * gets out of sync.
+ */
+ netlogon_creds_cli_check_cleanup(req, status);
+ tevent_req_done(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
+ /*
+ * Note that the negotiated flags are already checked
+ * for our required flags after the ServerAuthenticate3/2 call.
+ */
+ uint32_t negotiated = state->creds->negotiate_flags;
+
+ if (negotiated & NETLOGON_NEG_SUPPORTS_AES) {
+ /*
+ * If we have negotiated NETLOGON_NEG_SUPPORTS_AES
+ * already, we expect this to work!
+ */
+ status = NT_STATUS_DOWNGRADE_DETECTED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+
+ /*
+ * This is ok, the server does not support
+ * NETLOGON_NEG_SUPPORTS_AES.
+ *
+ * netr_LogonGetCapabilities() was
+ * netr_LogonDummyRoutine1() before
+ * NETLOGON_NEG_SUPPORTS_AES was invented.
+ */
+ netlogon_creds_cli_check_cleanup(req, result);
+ tevent_req_done(req);
+ return;
+ }
+
+ ok = netlogon_creds_client_check(state->creds, &state->rep_auth.cred);
+ if (!ok) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_check_cleanup(req, result);
+ return;
+ }
+
+ if (state->caps.server_capabilities != state->creds->negotiate_flags) {
+ status = NT_STATUS_DOWNGRADE_DETECTED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+
+ /*
+ * This is the key check that makes this check secure. If we
+ * get OK here (rather than NOT_SUPPORTED), then the server
+ * did support AES. If the server only proposed STRONG_KEYS
+ * and not AES, then it should have failed with
+ * NOT_IMPLEMENTED. We always send AES as a client, so the
+ * server should always have returned it.
+ */
+ if (!(state->caps.server_capabilities & NETLOGON_NEG_SUPPORTS_AES)) {
+ status = NT_STATUS_DOWNGRADE_DETECTED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+
+ status = netlogon_creds_cli_store_internal(state->context,
+ state->creds);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_check_recv(struct tevent_req *req,
+ union netr_Capabilities *capabilities)
+{
+ struct netlogon_creds_cli_check_state *state = tevent_req_data(
+ req, struct netlogon_creds_cli_check_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ netlogon_creds_cli_check_cleanup(req, status);
+ tevent_req_received(req);
+ return status;
+ }
+
+ if (capabilities != NULL) {
+ *capabilities = state->caps;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_check(struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ union netr_Capabilities *capabilities)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = netlogon_creds_cli_check_send(frame, ev, context, b);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_check_recv(req, capabilities);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct netlogon_creds_cli_ServerPasswordSet_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+ uint32_t old_timeout;
+
+ char *srv_name_slash;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ struct samr_CryptPassword samr_crypt_password;
+ struct netr_CryptPassword netr_crypt_password;
+ struct samr_Password samr_password;
+
+ struct netlogon_creds_CredentialState *creds;
+ struct netlogon_creds_CredentialState tmp_creds;
+ struct netr_Authenticator req_auth;
+ struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_ServerPasswordSet_cleanup(struct tevent_req *req,
+ NTSTATUS status);
+static void netlogon_creds_cli_ServerPasswordSet_locked(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_ServerPasswordSet_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ const DATA_BLOB *new_password,
+ const uint32_t *new_version)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_ServerPasswordSet_state *state;
+ struct tevent_req *subreq;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_ServerPasswordSet_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->context = context;
+ state->binding_handle = b;
+
+ if (new_password->length < 14) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * netr_ServerPasswordSet
+ */
+ mdfour(state->samr_password.hash, new_password->data, new_password->length);
+
+ /*
+ * netr_ServerPasswordSet2
+ */
+ ok = set_pw_in_buffer(state->samr_crypt_password.data,
+ new_password);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return tevent_req_post(req, ev);
+ }
+
+ if (new_version != NULL) {
+ struct NL_PASSWORD_VERSION version;
+ uint32_t len = IVAL(state->samr_crypt_password.data, 512);
+ uint32_t ofs = 512 - len;
+ uint8_t *p;
+
+ if (len > 500) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return tevent_req_post(req, ev);
+ }
+ ofs -= 12;
+
+ version.ReservedField = 0;
+ version.PasswordVersionNumber = *new_version;
+ version.PasswordVersionPresent =
+ NETLOGON_PASSWORD_VERSION_NUMBER_PRESENT;
+
+ p = state->samr_crypt_password.data + ofs;
+ SIVAL(p, 0, version.ReservedField);
+ SIVAL(p, 4, version.PasswordVersionNumber);
+ SIVAL(p, 8, version.PasswordVersionPresent);
+ }
+
+ state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+ context->server.computer);
+ if (tevent_req_nomem(state->srv_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ dcerpc_binding_handle_auth_info(state->binding_handle,
+ &state->auth_type,
+ &state->auth_level);
+
+ subreq = netlogon_creds_cli_lock_send(state, state->ev,
+ state->context);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_ServerPasswordSet_locked,
+ req);
+
+ return req;
+}
+
+static void netlogon_creds_cli_ServerPasswordSet_cleanup(struct tevent_req *req,
+ NTSTATUS status)
+{
+ struct netlogon_creds_cli_ServerPasswordSet_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_ServerPasswordSet_state);
+
+ if (state->creds == NULL) {
+ return;
+ }
+
+ dcerpc_binding_handle_set_timeout(state->binding_handle,
+ state->old_timeout);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+ TALLOC_FREE(state->creds);
+ return;
+ }
+
+ netlogon_creds_cli_delete(state->context, state->creds);
+ TALLOC_FREE(state->creds);
+}
+
+static void netlogon_creds_cli_ServerPasswordSet_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_ServerPasswordSet_locked(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_ServerPasswordSet_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_ServerPasswordSet_state);
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_lock_recv(subreq, state,
+ &state->creds);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+ switch (state->auth_level) {
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ break;
+ default:
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ } else {
+ uint32_t tmp = state->creds->negotiate_flags;
+
+ if (tmp & NETLOGON_NEG_AUTHENTICATED_RPC) {
+ /*
+ * if DCERPC_AUTH_TYPE_SCHANNEL is supported
+ * it should be used, which means
+ * we had a chance to verify no downgrade
+ * happened.
+ *
+ * This relies on netlogon_creds_cli_check*
+ * being called before, as first request after
+ * the DCERPC bind.
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ }
+
+ state->old_timeout = dcerpc_binding_handle_set_timeout(
+ state->binding_handle, 600000);
+
+ /*
+ * we defer all callbacks in order to cleanup
+ * the database record.
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ state->tmp_creds = *state->creds;
+ status = netlogon_creds_client_authenticator(&state->tmp_creds,
+ &state->req_auth);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ ZERO_STRUCT(state->rep_auth);
+
+ if (state->tmp_creds.negotiate_flags & NETLOGON_NEG_PASSWORD_SET2) {
+
+ if (state->tmp_creds.negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ status = netlogon_creds_aes_encrypt(&state->tmp_creds,
+ state->samr_crypt_password.data,
+ 516);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+ } else {
+ status = netlogon_creds_arcfour_crypt(&state->tmp_creds,
+ state->samr_crypt_password.data,
+ 516);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+ }
+
+ memcpy(state->netr_crypt_password.data,
+ state->samr_crypt_password.data, 512);
+ state->netr_crypt_password.length =
+ IVAL(state->samr_crypt_password.data, 512);
+
+ subreq = dcerpc_netr_ServerPasswordSet2_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->tmp_creds.account_name,
+ state->tmp_creds.secure_channel_type,
+ state->tmp_creds.computer_name,
+ &state->req_auth,
+ &state->rep_auth,
+ &state->netr_crypt_password);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+ } else {
+ status = netlogon_creds_des_encrypt(&state->tmp_creds,
+ &state->samr_password);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+
+ subreq = dcerpc_netr_ServerPasswordSet_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->tmp_creds.account_name,
+ state->tmp_creds.secure_channel_type,
+ state->tmp_creds.computer_name,
+ &state->req_auth,
+ &state->rep_auth,
+ &state->samr_password);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_ServerPasswordSet_done,
+ req);
+}
+
+static void netlogon_creds_cli_ServerPasswordSet_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_ServerPasswordSet_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_ServerPasswordSet_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+
+ if (state->tmp_creds.negotiate_flags & NETLOGON_NEG_PASSWORD_SET2) {
+ status = dcerpc_netr_ServerPasswordSet2_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+ } else {
+ status = dcerpc_netr_ServerPasswordSet_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+ }
+
+ ok = netlogon_creds_client_check(&state->tmp_creds,
+ &state->rep_auth.cred);
+ if (!ok) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, result);
+ return;
+ }
+
+ dcerpc_binding_handle_set_timeout(state->binding_handle,
+ state->old_timeout);
+
+ *state->creds = state->tmp_creds;
+ status = netlogon_creds_cli_store(state->context,
+ state->creds);
+ TALLOC_FREE(state->creds);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_ServerPasswordSet_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_ServerPasswordSet(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ const DATA_BLOB *new_password,
+ const uint32_t *new_version)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = netlogon_creds_cli_ServerPasswordSet_send(frame, ev, context, b,
+ new_password,
+ new_version);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_ServerPasswordSet_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct netlogon_creds_cli_LogonSamLogon_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+
+ char *srv_name_slash;
+
+ enum netr_LogonInfoClass logon_level;
+ const union netr_LogonLevel *const_logon;
+ union netr_LogonLevel *logon;
+ uint32_t flags;
+
+ uint16_t validation_level;
+ union netr_Validation *validation;
+ uint8_t authoritative;
+
+ /*
+ * do we need encryption at the application layer?
+ */
+ bool user_encrypt;
+ bool try_logon_ex;
+ bool try_validation6;
+
+ /*
+ * the read only credentials before we started the operation
+ * used for netr_LogonSamLogonEx() if required (validation_level = 3).
+ */
+ struct netlogon_creds_CredentialState *ro_creds;
+
+ /*
+ * The (locked) credentials used for the credential chain
+ * used for netr_LogonSamLogonWithFlags() or
+ * netr_LogonSamLogonWith().
+ */
+ struct netlogon_creds_CredentialState *lk_creds;
+
+ /*
+ * While we have locked the global credentials (lk_creds above)
+ * we operate an a temporary copy, because a server
+ * may not support netr_LogonSamLogonWithFlags() and
+ * didn't process our netr_Authenticator, so we need to
+ * restart from lk_creds.
+ */
+ struct netlogon_creds_CredentialState tmp_creds;
+ struct netr_Authenticator req_auth;
+ struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_LogonSamLogon_start(struct tevent_req *req);
+static void netlogon_creds_cli_LogonSamLogon_cleanup(struct tevent_req *req,
+ NTSTATUS status);
+
+struct tevent_req *netlogon_creds_cli_LogonSamLogon_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ enum netr_LogonInfoClass logon_level,
+ const union netr_LogonLevel *logon,
+ uint32_t flags)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_LogonSamLogon_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_LogonSamLogon_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->context = context;
+ state->binding_handle = b;
+
+ state->logon_level = logon_level;
+ state->const_logon = logon;
+ state->flags = flags;
+
+ state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+ context->server.computer);
+ if (tevent_req_nomem(state->srv_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ switch (logon_level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonServiceTransitiveInformation:
+ case NetlogonGenericInformation:
+ state->user_encrypt = true;
+ break;
+
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+ break;
+ }
+
+ state->validation = talloc_zero(state, union netr_Validation);
+ if (tevent_req_nomem(state->validation, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ netlogon_creds_cli_LogonSamLogon_start(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * we defer all callbacks in order to cleanup
+ * the database record.
+ */
+ tevent_req_defer_callback(req, state->ev);
+ return req;
+}
+
+static void netlogon_creds_cli_LogonSamLogon_cleanup(struct tevent_req *req,
+ NTSTATUS status)
+{
+ struct netlogon_creds_cli_LogonSamLogon_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_LogonSamLogon_state);
+
+ if (state->lk_creds == NULL) {
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ /*
+ * This is a hack to recover from a bug in old
+ * Samba servers, when LogonSamLogonEx() fails:
+ *
+ * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
+ *
+ * All following request will get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
+ *
+ * A second bug generates NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE,
+ * instead of NT_STATUS_ACCESS_DENIED or NT_STATUS_RPC_SEC_PKG_ERROR
+ * If the sign/seal check fails.
+ *
+ * In that case we need to cleanup the netlogon session.
+ *
+ * It's the job of the caller to disconnect the current
+ * connection, if netlogon_creds_cli_LogonSamLogon()
+ * returns NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
+ */
+ if (!state->context->server.try_logon_with) {
+ status = NT_STATUS_NETWORK_ACCESS_DENIED;
+ }
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+ TALLOC_FREE(state->lk_creds);
+ return;
+ }
+
+ netlogon_creds_cli_delete(state->context, state->lk_creds);
+ TALLOC_FREE(state->lk_creds);
+}
+
+static void netlogon_creds_cli_LogonSamLogon_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_LogonSamLogon_start(struct tevent_req *req)
+{
+ struct netlogon_creds_cli_LogonSamLogon_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_LogonSamLogon_state);
+ struct tevent_req *subreq;
+ NTSTATUS status;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ TALLOC_FREE(state->ro_creds);
+ TALLOC_FREE(state->logon);
+ ZERO_STRUCTP(state->validation);
+
+ dcerpc_binding_handle_auth_info(state->binding_handle,
+ &auth_type, &auth_level);
+
+ state->try_logon_ex = state->context->server.try_logon_ex;
+ state->try_validation6 = state->context->server.try_validation6;
+
+ if (auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
+ state->try_logon_ex = false;
+ }
+
+ if (auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
+ state->try_validation6 = false;
+ }
+
+ if (state->try_logon_ex) {
+ if (state->try_validation6) {
+ state->validation_level = 6;
+ } else {
+ state->validation_level = 3;
+ state->user_encrypt = true;
+ }
+
+ state->logon = netlogon_creds_shallow_copy_logon(state,
+ state->logon_level,
+ state->const_logon);
+ if (tevent_req_nomem(state->logon, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ if (state->user_encrypt) {
+ status = netlogon_creds_cli_get(state->context,
+ state,
+ &state->ro_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ status = netlogon_creds_encrypt_samlogon_logon(state->ro_creds,
+ state->logon_level,
+ state->logon);
+ if (!NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ }
+
+ subreq = dcerpc_netr_LogonSamLogonEx_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->context->client.computer,
+ state->logon_level,
+ state->logon,
+ state->validation_level,
+ state->validation,
+ &state->authoritative,
+ &state->flags);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_LogonSamLogon_done,
+ req);
+ return;
+ }
+
+ if (state->lk_creds == NULL) {
+ subreq = netlogon_creds_cli_lock_send(state, state->ev,
+ state->context);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_LogonSamLogon_done,
+ req);
+ return;
+ }
+
+ state->tmp_creds = *state->lk_creds;
+ status = netlogon_creds_client_authenticator(&state->tmp_creds,
+ &state->req_auth);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ ZERO_STRUCT(state->rep_auth);
+
+ state->logon = netlogon_creds_shallow_copy_logon(state,
+ state->logon_level,
+ state->const_logon);
+ if (tevent_req_nomem(state->logon, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ status = netlogon_creds_encrypt_samlogon_logon(&state->tmp_creds,
+ state->logon_level,
+ state->logon);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ state->validation_level = 3;
+
+ if (state->context->server.try_logon_with) {
+ subreq = dcerpc_netr_LogonSamLogonWithFlags_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->context->client.computer,
+ &state->req_auth,
+ &state->rep_auth,
+ state->logon_level,
+ state->logon,
+ state->validation_level,
+ state->validation,
+ &state->authoritative,
+ &state->flags);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ } else {
+ state->flags = 0;
+
+ subreq = dcerpc_netr_LogonSamLogon_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->context->client.computer,
+ &state->req_auth,
+ &state->rep_auth,
+ state->logon_level,
+ state->logon,
+ state->validation_level,
+ state->validation,
+ &state->authoritative);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_LogonSamLogon_done,
+ req);
+}
+
+static void netlogon_creds_cli_LogonSamLogon_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_LogonSamLogon_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_LogonSamLogon_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+
+ if (state->try_logon_ex) {
+ status = dcerpc_netr_LogonSamLogonEx_recv(subreq,
+ state->validation,
+ &result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ state->context->server.try_validation6 = false;
+ state->context->server.try_logon_ex = false;
+ netlogon_creds_cli_LogonSamLogon_start(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ if ((state->validation_level == 6) &&
+ (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)))
+ {
+ state->context->server.try_validation6 = false;
+ netlogon_creds_cli_LogonSamLogon_start(req);
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, result);
+ return;
+ }
+
+ if (state->ro_creds == NULL) {
+ tevent_req_done(req);
+ return;
+ }
+
+ ok = netlogon_creds_cli_validate(state->context, state->ro_creds);
+ if (!ok) {
+ /*
+ * We got a race, lets retry with on authenticator
+ * protection.
+ *
+ * netlogon_creds_cli_LogonSamLogon_start()
+ * will TALLOC_FREE(state->ro_creds);
+ */
+ state->try_logon_ex = false;
+ netlogon_creds_cli_LogonSamLogon_start(req);
+ return;
+ }
+
+ status = netlogon_creds_decrypt_samlogon_validation(state->ro_creds,
+ state->validation_level,
+ state->validation);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+ }
+
+ if (state->lk_creds == NULL) {
+ status = netlogon_creds_cli_lock_recv(subreq, state,
+ &state->lk_creds);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ netlogon_creds_cli_LogonSamLogon_start(req);
+ return;
+ }
+
+ if (state->context->server.try_logon_with) {
+ status = dcerpc_netr_LogonSamLogonWithFlags_recv(subreq,
+ state->validation,
+ &result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ state->context->server.try_logon_with = false;
+ netlogon_creds_cli_LogonSamLogon_start(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ } else {
+ status = dcerpc_netr_LogonSamLogon_recv(subreq,
+ state->validation,
+ &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ }
+
+ ok = netlogon_creds_client_check(&state->tmp_creds,
+ &state->rep_auth.cred);
+ if (!ok) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ *state->lk_creds = state->tmp_creds;
+ status = netlogon_creds_cli_store(state->context,
+ state->lk_creds);
+ TALLOC_FREE(state->lk_creds);
+
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, result);
+ return;
+ }
+
+ status = netlogon_creds_decrypt_samlogon_validation(&state->tmp_creds,
+ state->validation_level,
+ state->validation);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, result);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_LogonSamLogon_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint16_t *validation_level,
+ union netr_Validation **validation,
+ uint8_t *authoritative,
+ uint32_t *flags)
+{
+ struct netlogon_creds_cli_LogonSamLogon_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_LogonSamLogon_state);
+ NTSTATUS status;
+
+ /* authoritative is also returned on error */
+ *authoritative = state->authoritative;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ tevent_req_received(req);
+ return status;
+ }
+
+ *validation_level = state->validation_level;
+ *validation = talloc_move(mem_ctx, &state->validation);
+ *flags = state->flags;
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_LogonSamLogon(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ enum netr_LogonInfoClass logon_level,
+ const union netr_LogonLevel *logon,
+ TALLOC_CTX *mem_ctx,
+ uint16_t *validation_level,
+ union netr_Validation **validation,
+ uint8_t *authoritative,
+ uint32_t *flags)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = netlogon_creds_cli_LogonSamLogon_send(frame, ev, context, b,
+ logon_level, logon,
+ *flags);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_LogonSamLogon_recv(req, mem_ctx,
+ validation_level,
+ validation,
+ authoritative,
+ flags);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+
+ char *srv_name_slash;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ const char *site_name;
+ uint32_t dns_ttl;
+ struct NL_DNS_NAME_INFO_ARRAY *dns_names;
+
+ struct netlogon_creds_CredentialState *creds;
+ struct netlogon_creds_CredentialState tmp_creds;
+ struct netr_Authenticator req_auth;
+ struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(struct tevent_req *req,
+ NTSTATUS status);
+static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_locked(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ const char *site_name,
+ uint32_t dns_ttl,
+ struct NL_DNS_NAME_INFO_ARRAY *dns_names)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state *state;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->context = context;
+ state->binding_handle = b;
+
+ state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+ context->server.computer);
+ if (tevent_req_nomem(state->srv_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->site_name = site_name;
+ state->dns_ttl = dns_ttl;
+ state->dns_names = dns_names;
+
+ dcerpc_binding_handle_auth_info(state->binding_handle,
+ &state->auth_type,
+ &state->auth_level);
+
+ subreq = netlogon_creds_cli_lock_send(state, state->ev,
+ state->context);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_locked,
+ req);
+
+ return req;
+}
+
+static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(struct tevent_req *req,
+ NTSTATUS status)
+{
+ struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state);
+
+ if (state->creds == NULL) {
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+ TALLOC_FREE(state->creds);
+ return;
+ }
+
+ netlogon_creds_cli_delete(state->context, state->creds);
+ TALLOC_FREE(state->creds);
+}
+
+static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_locked(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state);
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_lock_recv(subreq, state,
+ &state->creds);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+ switch (state->auth_level) {
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ break;
+ default:
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ } else {
+ uint32_t tmp = state->creds->negotiate_flags;
+
+ if (tmp & NETLOGON_NEG_AUTHENTICATED_RPC) {
+ /*
+ * if DCERPC_AUTH_TYPE_SCHANNEL is supported
+ * it should be used, which means
+ * we had a chance to verify no downgrade
+ * happened.
+ *
+ * This relies on netlogon_creds_cli_check*
+ * being called before, as first request after
+ * the DCERPC bind.
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ }
+
+ /*
+ * we defer all callbacks in order to cleanup
+ * the database record.
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ state->tmp_creds = *state->creds;
+ status = netlogon_creds_client_authenticator(&state->tmp_creds,
+ &state->req_auth);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ ZERO_STRUCT(state->rep_auth);
+
+ subreq = dcerpc_netr_DsrUpdateReadOnlyServerDnsRecords_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->tmp_creds.computer_name,
+ &state->req_auth,
+ &state->rep_auth,
+ state->site_name,
+ state->dns_ttl,
+ state->dns_names);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status);
+ return;
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_done,
+ req);
+}
+
+static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+
+ /*
+ * We use state->dns_names as the memory context, as this is
+ * the only in/out variable and it has been overwritten by the
+ * out parameter from the server.
+ *
+ * We need to preserve the return value until the caller can use it.
+ */
+ status = dcerpc_netr_DsrUpdateReadOnlyServerDnsRecords_recv(subreq, state->dns_names,
+ &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status);
+ return;
+ }
+
+ ok = netlogon_creds_client_check(&state->tmp_creds,
+ &state->rep_auth.cred);
+ if (!ok) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status);
+ return;
+ }
+
+ *state->creds = state->tmp_creds;
+ status = netlogon_creds_cli_store(state->context,
+ state->creds);
+ TALLOC_FREE(state->creds);
+
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status);
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, result);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status);
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ const char *site_name,
+ uint32_t dns_ttl,
+ struct NL_DNS_NAME_INFO_ARRAY *dns_names)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_send(frame, ev, context, b,
+ site_name,
+ dns_ttl,
+ dns_names);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct netlogon_creds_cli_ServerGetTrustInfo_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+
+ char *srv_name_slash;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ struct samr_Password new_owf_password;
+ struct samr_Password old_owf_password;
+ struct netr_TrustInfo *trust_info;
+
+ struct netlogon_creds_CredentialState *creds;
+ struct netlogon_creds_CredentialState tmp_creds;
+ struct netr_Authenticator req_auth;
+ struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_ServerGetTrustInfo_cleanup(struct tevent_req *req,
+ NTSTATUS status);
+static void netlogon_creds_cli_ServerGetTrustInfo_locked(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_ServerGetTrustInfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_ServerGetTrustInfo_state *state;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_ServerGetTrustInfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->context = context;
+ state->binding_handle = b;
+
+ state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+ context->server.computer);
+ if (tevent_req_nomem(state->srv_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ dcerpc_binding_handle_auth_info(state->binding_handle,
+ &state->auth_type,
+ &state->auth_level);
+
+ subreq = netlogon_creds_cli_lock_send(state, state->ev,
+ state->context);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_ServerGetTrustInfo_locked,
+ req);
+
+ return req;
+}
+
+static void netlogon_creds_cli_ServerGetTrustInfo_cleanup(struct tevent_req *req,
+ NTSTATUS status)
+{
+ struct netlogon_creds_cli_ServerGetTrustInfo_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_ServerGetTrustInfo_state);
+
+ if (state->creds == NULL) {
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+ TALLOC_FREE(state->creds);
+ return;
+ }
+
+ netlogon_creds_cli_delete(state->context, state->creds);
+ TALLOC_FREE(state->creds);
+}
+
+static void netlogon_creds_cli_ServerGetTrustInfo_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_ServerGetTrustInfo_locked(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_ServerGetTrustInfo_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_ServerGetTrustInfo_state);
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_lock_recv(subreq, state,
+ &state->creds);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+ switch (state->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ break;
+ default:
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ } else {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+
+ /*
+ * we defer all callbacks in order to cleanup
+ * the database record.
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ state->tmp_creds = *state->creds;
+ status = netlogon_creds_client_authenticator(&state->tmp_creds,
+ &state->req_auth);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ ZERO_STRUCT(state->rep_auth);
+
+ subreq = dcerpc_netr_ServerGetTrustInfo_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->tmp_creds.account_name,
+ state->tmp_creds.secure_channel_type,
+ state->tmp_creds.computer_name,
+ &state->req_auth,
+ &state->rep_auth,
+ &state->new_owf_password,
+ &state->old_owf_password,
+ &state->trust_info);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, status);
+ return;
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_ServerGetTrustInfo_done,
+ req);
+}
+
+static void netlogon_creds_cli_ServerGetTrustInfo_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_ServerGetTrustInfo_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_ServerGetTrustInfo_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ const struct samr_Password zero = {};
+ bool cmp;
+ bool ok;
+
+ /*
+ * We use state->dns_names as the memory context, as this is
+ * the only in/out variable and it has been overwritten by the
+ * out parameter from the server.
+ *
+ * We need to preserve the return value until the caller can use it.
+ */
+ status = dcerpc_netr_ServerGetTrustInfo_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, status);
+ return;
+ }
+
+ ok = netlogon_creds_client_check(&state->tmp_creds,
+ &state->rep_auth.cred);
+ if (!ok) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, status);
+ return;
+ }
+
+ cmp = mem_equal_const_time(state->new_owf_password.hash,
+ zero.hash, sizeof(zero.hash));
+ if (!cmp) {
+ status = netlogon_creds_des_decrypt(&state->tmp_creds,
+ &state->new_owf_password);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, status);
+ return;
+ }
+ }
+ cmp = mem_equal_const_time(state->old_owf_password.hash,
+ zero.hash, sizeof(zero.hash));
+ if (!cmp) {
+ status = netlogon_creds_des_decrypt(&state->tmp_creds,
+ &state->old_owf_password);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, status);
+ return;
+ }
+ }
+
+ *state->creds = state->tmp_creds;
+ status = netlogon_creds_cli_store(state->context,
+ state->creds);
+ TALLOC_FREE(state->creds);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, status);
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, result);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_ServerGetTrustInfo_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct samr_Password *new_owf_password,
+ struct samr_Password *old_owf_password,
+ struct netr_TrustInfo **trust_info)
+{
+ struct netlogon_creds_cli_ServerGetTrustInfo_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_ServerGetTrustInfo_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, status);
+ tevent_req_received(req);
+ return status;
+ }
+
+ if (new_owf_password != NULL) {
+ *new_owf_password = state->new_owf_password;
+ }
+ if (old_owf_password != NULL) {
+ *old_owf_password = state->old_owf_password;
+ }
+ if (trust_info != NULL) {
+ *trust_info = talloc_move(mem_ctx, &state->trust_info);
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_ServerGetTrustInfo(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ struct samr_Password *new_owf_password,
+ struct samr_Password *old_owf_password,
+ struct netr_TrustInfo **trust_info)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = netlogon_creds_cli_ServerGetTrustInfo_send(frame, ev, context, b);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_ServerGetTrustInfo_recv(req,
+ mem_ctx,
+ new_owf_password,
+ old_owf_password,
+ trust_info);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct netlogon_creds_cli_GetForestTrustInformation_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+
+ char *srv_name_slash;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ uint32_t flags;
+ struct lsa_ForestTrustInformation *forest_trust_info;
+
+ struct netlogon_creds_CredentialState *creds;
+ struct netlogon_creds_CredentialState tmp_creds;
+ struct netr_Authenticator req_auth;
+ struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_GetForestTrustInformation_cleanup(struct tevent_req *req,
+ NTSTATUS status);
+static void netlogon_creds_cli_GetForestTrustInformation_locked(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_GetForestTrustInformation_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_GetForestTrustInformation_state *state;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_GetForestTrustInformation_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->context = context;
+ state->binding_handle = b;
+
+ state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+ context->server.computer);
+ if (tevent_req_nomem(state->srv_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->flags = 0;
+
+ dcerpc_binding_handle_auth_info(state->binding_handle,
+ &state->auth_type,
+ &state->auth_level);
+
+ subreq = netlogon_creds_cli_lock_send(state, state->ev,
+ state->context);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_GetForestTrustInformation_locked,
+ req);
+
+ return req;
+}
+
+static void netlogon_creds_cli_GetForestTrustInformation_cleanup(struct tevent_req *req,
+ NTSTATUS status)
+{
+ struct netlogon_creds_cli_GetForestTrustInformation_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_GetForestTrustInformation_state);
+
+ if (state->creds == NULL) {
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+ TALLOC_FREE(state->creds);
+ return;
+ }
+
+ netlogon_creds_cli_delete(state->context, state->creds);
+ TALLOC_FREE(state->creds);
+}
+
+static void netlogon_creds_cli_GetForestTrustInformation_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_GetForestTrustInformation_locked(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_GetForestTrustInformation_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_GetForestTrustInformation_state);
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_lock_recv(subreq, state,
+ &state->creds);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+ switch (state->auth_level) {
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ break;
+ default:
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ } else {
+ uint32_t tmp = state->creds->negotiate_flags;
+
+ if (tmp & NETLOGON_NEG_AUTHENTICATED_RPC) {
+ /*
+ * if DCERPC_AUTH_TYPE_SCHANNEL is supported
+ * it should be used, which means
+ * we had a chance to verify no downgrade
+ * happened.
+ *
+ * This relies on netlogon_creds_cli_check*
+ * being called before, as first request after
+ * the DCERPC bind.
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ }
+
+ /*
+ * we defer all callbacks in order to cleanup
+ * the database record.
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ state->tmp_creds = *state->creds;
+ status = netlogon_creds_client_authenticator(&state->tmp_creds,
+ &state->req_auth);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ ZERO_STRUCT(state->rep_auth);
+
+ subreq = dcerpc_netr_GetForestTrustInformation_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->tmp_creds.computer_name,
+ &state->req_auth,
+ &state->rep_auth,
+ state->flags,
+ &state->forest_trust_info);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_GetForestTrustInformation_cleanup(req, status);
+ return;
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_GetForestTrustInformation_done,
+ req);
+}
+
+static void netlogon_creds_cli_GetForestTrustInformation_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_GetForestTrustInformation_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_GetForestTrustInformation_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+
+ /*
+ * We use state->dns_names as the memory context, as this is
+ * the only in/out variable and it has been overwritten by the
+ * out parameter from the server.
+ *
+ * We need to preserve the return value until the caller can use it.
+ */
+ status = dcerpc_netr_GetForestTrustInformation_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_GetForestTrustInformation_cleanup(req, status);
+ return;
+ }
+
+ ok = netlogon_creds_client_check(&state->tmp_creds,
+ &state->rep_auth.cred);
+ if (!ok) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_GetForestTrustInformation_cleanup(req, status);
+ return;
+ }
+
+ *state->creds = state->tmp_creds;
+ status = netlogon_creds_cli_store(state->context,
+ state->creds);
+ TALLOC_FREE(state->creds);
+
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_GetForestTrustInformation_cleanup(req, status);
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_GetForestTrustInformation_cleanup(req, result);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_GetForestTrustInformation_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_ForestTrustInformation **forest_trust_info)
+{
+ struct netlogon_creds_cli_GetForestTrustInformation_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_GetForestTrustInformation_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ netlogon_creds_cli_GetForestTrustInformation_cleanup(req, status);
+ tevent_req_received(req);
+ return status;
+ }
+
+ *forest_trust_info = talloc_move(mem_ctx, &state->forest_trust_info);
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_GetForestTrustInformation(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_ForestTrustInformation **forest_trust_info)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = netlogon_creds_cli_GetForestTrustInformation_send(frame, ev, context, b);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_GetForestTrustInformation_recv(req,
+ mem_ctx,
+ forest_trust_info);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+struct netlogon_creds_cli_SendToSam_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+
+ char *srv_name_slash;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ DATA_BLOB opaque;
+
+ struct netlogon_creds_CredentialState *creds;
+ struct netlogon_creds_CredentialState tmp_creds;
+ struct netr_Authenticator req_auth;
+ struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_SendToSam_cleanup(struct tevent_req *req,
+ NTSTATUS status);
+static void netlogon_creds_cli_SendToSam_locked(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_SendToSam_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ struct netr_SendToSamBase *message)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_SendToSam_state *state;
+ struct tevent_req *subreq;
+ enum ndr_err_code ndr_err;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_SendToSam_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->context = context;
+ state->binding_handle = b;
+
+ state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+ context->server.computer);
+ if (tevent_req_nomem(state->srv_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ ndr_err = ndr_push_struct_blob(&state->opaque, mem_ctx, message,
+ (ndr_push_flags_fn_t)ndr_push_netr_SendToSamBase);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ dcerpc_binding_handle_auth_info(state->binding_handle,
+ &state->auth_type,
+ &state->auth_level);
+
+ subreq = netlogon_creds_cli_lock_send(state, state->ev,
+ state->context);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_SendToSam_locked,
+ req);
+
+ return req;
+}
+
+static void netlogon_creds_cli_SendToSam_cleanup(struct tevent_req *req,
+ NTSTATUS status)
+{
+ struct netlogon_creds_cli_SendToSam_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_SendToSam_state);
+
+ if (state->creds == NULL) {
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+ TALLOC_FREE(state->creds);
+ return;
+ }
+
+ netlogon_creds_cli_delete(state->context, state->creds);
+ TALLOC_FREE(state->creds);
+}
+
+static void netlogon_creds_cli_SendToSam_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_SendToSam_locked(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_SendToSam_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_SendToSam_state);
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_lock_recv(subreq, state,
+ &state->creds);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+ switch (state->auth_level) {
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ break;
+ default:
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ } else {
+ uint32_t tmp = state->creds->negotiate_flags;
+
+ if (tmp & NETLOGON_NEG_AUTHENTICATED_RPC) {
+ /*
+ * if DCERPC_AUTH_TYPE_SCHANNEL is supported
+ * it should be used, which means
+ * we had a chance to verify no downgrade
+ * happened.
+ *
+ * This relies on netlogon_creds_cli_check*
+ * being called before, as first request after
+ * the DCERPC bind.
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ }
+
+ /*
+ * we defer all callbacks in order to cleanup
+ * the database record.
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ state->tmp_creds = *state->creds;
+ status = netlogon_creds_client_authenticator(&state->tmp_creds,
+ &state->req_auth);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ ZERO_STRUCT(state->rep_auth);
+
+ if (state->tmp_creds.negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ status = netlogon_creds_aes_encrypt(&state->tmp_creds,
+ state->opaque.data,
+ state->opaque.length);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_SendToSam_cleanup(req, status);
+ return;
+ }
+ } else {
+ status = netlogon_creds_arcfour_crypt(&state->tmp_creds,
+ state->opaque.data,
+ state->opaque.length);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_SendToSam_cleanup(req, status);
+ return;
+ }
+ }
+
+ subreq = dcerpc_netr_NetrLogonSendToSam_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->tmp_creds.computer_name,
+ &state->req_auth,
+ &state->rep_auth,
+ state->opaque.data,
+ state->opaque.length);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_SendToSam_cleanup(req, status);
+ return;
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_SendToSam_done,
+ req);
+}
+
+static void netlogon_creds_cli_SendToSam_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_SendToSam_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_SendToSam_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+
+ status = dcerpc_netr_NetrLogonSendToSam_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_SendToSam_cleanup(req, status);
+ return;
+ }
+
+ ok = netlogon_creds_client_check(&state->tmp_creds,
+ &state->rep_auth.cred);
+ if (!ok) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_SendToSam_cleanup(req, status);
+ return;
+ }
+
+ *state->creds = state->tmp_creds;
+ status = netlogon_creds_cli_store(state->context,
+ state->creds);
+ TALLOC_FREE(state->creds);
+
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_SendToSam_cleanup(req, status);
+ return;
+ }
+
+ /*
+ * Creds must be stored before we send back application errors
+ * e.g. NT_STATUS_NOT_IMPLEMENTED
+ */
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_SendToSam_cleanup(req, result);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_SendToSam_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ netlogon_creds_cli_SendToSam_cleanup(req, status);
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_SendToSam(struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ struct netr_SendToSamBase *message)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = netlogon_creds_cli_SendToSam_send(frame, ev, context, b, message);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_SendToSam_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct netlogon_creds_cli_LogonGetDomainInfo_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+
+ char *srv_name_slash;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ uint32_t level;
+ union netr_WorkstationInfo *query;
+ union netr_DomainInfo *info;
+
+ struct netlogon_creds_CredentialState *creds;
+ struct netlogon_creds_CredentialState tmp_creds;
+ struct netr_Authenticator req_auth;
+ struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_LogonGetDomainInfo_cleanup(struct tevent_req *req,
+ NTSTATUS status);
+static void netlogon_creds_cli_LogonGetDomainInfo_locked(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_LogonGetDomainInfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ uint32_t level,
+ union netr_WorkstationInfo *query)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_LogonGetDomainInfo_state *state;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_LogonGetDomainInfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->context = context;
+ state->binding_handle = b;
+
+ state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+ context->server.computer);
+ if (tevent_req_nomem(state->srv_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->level = level;
+ state->query = query;
+ state->info = talloc_zero(state, union netr_DomainInfo);
+ if (tevent_req_nomem(state->info, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ dcerpc_binding_handle_auth_info(state->binding_handle,
+ &state->auth_type,
+ &state->auth_level);
+
+ subreq = netlogon_creds_cli_lock_send(state, state->ev,
+ state->context);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_LogonGetDomainInfo_locked,
+ req);
+
+ return req;
+}
+
+static void netlogon_creds_cli_LogonGetDomainInfo_cleanup(struct tevent_req *req,
+ NTSTATUS status)
+{
+ struct netlogon_creds_cli_LogonGetDomainInfo_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_LogonGetDomainInfo_state);
+
+ if (state->creds == NULL) {
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+ TALLOC_FREE(state->creds);
+ return;
+ }
+
+ netlogon_creds_cli_delete(state->context, state->creds);
+}
+
+static void netlogon_creds_cli_LogonGetDomainInfo_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_LogonGetDomainInfo_locked(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_LogonGetDomainInfo_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_LogonGetDomainInfo_state);
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_lock_recv(subreq, state,
+ &state->creds);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+ switch (state->auth_level) {
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ break;
+ default:
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ } else {
+ uint32_t tmp = state->creds->negotiate_flags;
+
+ if (tmp & NETLOGON_NEG_AUTHENTICATED_RPC) {
+ /*
+ * if DCERPC_AUTH_TYPE_SCHANNEL is supported
+ * it should be used, which means
+ * we had a chance to verify no downgrade
+ * happened.
+ *
+ * This relies on netlogon_creds_cli_check*
+ * being called before, as first request after
+ * the DCERPC bind.
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return;
+ }
+ }
+
+ /*
+ * we defer all callbacks in order to cleanup
+ * the database record.
+ */
+ tevent_req_defer_callback(req, state->ev);
+
+ state->tmp_creds = *state->creds;
+ status = netlogon_creds_client_authenticator(&state->tmp_creds,
+ &state->req_auth);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ ZERO_STRUCT(state->rep_auth);
+
+ subreq = dcerpc_netr_LogonGetDomainInfo_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->tmp_creds.computer_name,
+ &state->req_auth,
+ &state->rep_auth,
+ state->level,
+ state->query,
+ state->info);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonGetDomainInfo_cleanup(req, status);
+ return;
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_LogonGetDomainInfo_done,
+ req);
+}
+
+static void netlogon_creds_cli_LogonGetDomainInfo_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_LogonGetDomainInfo_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_LogonGetDomainInfo_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+
+ /*
+ * We use state->dns_names as the memory context, as this is
+ * the only in/out variable and it has been overwritten by the
+ * out parameter from the server.
+ *
+ * We need to preserve the return value until the caller can use it.
+ */
+ status = dcerpc_netr_LogonGetDomainInfo_recv(subreq, state->info, &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonGetDomainInfo_cleanup(req, status);
+ return;
+ }
+
+ ok = netlogon_creds_client_check(&state->tmp_creds,
+ &state->rep_auth.cred);
+ if (!ok) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_LogonGetDomainInfo_cleanup(req, status);
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_LogonGetDomainInfo_cleanup(req, result);
+ return;
+ }
+
+ *state->creds = state->tmp_creds;
+ status = netlogon_creds_cli_store(state->context,
+ state->creds);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonGetDomainInfo_cleanup(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_LogonGetDomainInfo_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ union netr_DomainInfo **info)
+{
+ struct netlogon_creds_cli_LogonGetDomainInfo_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_LogonGetDomainInfo_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ netlogon_creds_cli_LogonGetDomainInfo_cleanup(req, status);
+ tevent_req_received(req);
+ return status;
+ }
+
+ *info = talloc_move(mem_ctx, &state->info);
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_LogonGetDomainInfo(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ uint32_t level,
+ union netr_WorkstationInfo *query,
+ union netr_DomainInfo **info)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = netlogon_creds_cli_LogonGetDomainInfo_send(frame, ev, context, b,
+ level, query);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_LogonGetDomainInfo_recv(req,
+ mem_ctx,
+ info);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/libcli/auth/netlogon_creds_cli.h b/libcli/auth/netlogon_creds_cli.h
new file mode 100644
index 0000000..600242e
--- /dev/null
+++ b/libcli/auth/netlogon_creds_cli.h
@@ -0,0 +1,236 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ module to store/fetch session keys for the schannel client
+
+ Copyright (C) Stefan Metzmacher 2013
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef NETLOGON_CREDS_CLI_H
+#define NETLOGON_CREDS_CLI_H
+
+#include "librpc/gen_ndr/dcerpc.h"
+#include "librpc/gen_ndr/schannel.h"
+
+struct netlogon_creds_cli_context;
+struct cli_credentials;
+struct messaging_context;
+struct dcerpc_binding_handle;
+struct db_context;
+
+NTSTATUS netlogon_creds_cli_set_global_db(struct loadparm_context *lp_ctx, struct db_context **db);
+NTSTATUS netlogon_creds_cli_open_global_db(struct loadparm_context *lp_ctx);
+void netlogon_creds_cli_close_global_db(void);
+
+void netlogon_creds_cli_warn_options(struct loadparm_context *lp_ctx);
+
+NTSTATUS netlogon_creds_cli_context_global(struct loadparm_context *lp_ctx,
+ struct messaging_context *msg_ctx,
+ const char *client_account,
+ enum netr_SchannelType type,
+ const char *server_computer,
+ const char *server_netbios_domain,
+ const char *server_dns_domain,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_context **_context);
+NTSTATUS netlogon_creds_bind_cli_credentials(
+ struct netlogon_creds_cli_context *context, TALLOC_CTX *mem_ctx,
+ struct cli_credentials **pcli_creds);
+
+char *netlogon_creds_cli_debug_string(
+ const struct netlogon_creds_cli_context *context,
+ TALLOC_CTX *mem_ctx);
+
+enum dcerpc_AuthLevel netlogon_creds_cli_auth_level(
+ struct netlogon_creds_cli_context *context);
+
+NTSTATUS netlogon_creds_cli_get(struct netlogon_creds_cli_context *context,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_CredentialState **_creds);
+bool netlogon_creds_cli_validate(struct netlogon_creds_cli_context *context,
+ const struct netlogon_creds_CredentialState *creds1);
+
+NTSTATUS netlogon_creds_cli_store(struct netlogon_creds_cli_context *context,
+ struct netlogon_creds_CredentialState *creds);
+NTSTATUS netlogon_creds_cli_delete(struct netlogon_creds_cli_context *context,
+ struct netlogon_creds_CredentialState *creds);
+NTSTATUS netlogon_creds_cli_delete_lck(
+ struct netlogon_creds_cli_context *context);
+
+struct tevent_req *netlogon_creds_cli_lock_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context);
+NTSTATUS netlogon_creds_cli_lock_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_CredentialState **creds);
+NTSTATUS netlogon_creds_cli_lock(struct netlogon_creds_cli_context *context,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_CredentialState **creds);
+
+struct netlogon_creds_cli_lck;
+
+enum netlogon_creds_cli_lck_type {
+ NETLOGON_CREDS_CLI_LCK_NONE,
+ NETLOGON_CREDS_CLI_LCK_SHARED,
+ NETLOGON_CREDS_CLI_LCK_EXCLUSIVE,
+};
+
+struct tevent_req *netlogon_creds_cli_lck_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ enum netlogon_creds_cli_lck_type type);
+NTSTATUS netlogon_creds_cli_lck_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_lck **lck);
+NTSTATUS netlogon_creds_cli_lck(
+ struct netlogon_creds_cli_context *context,
+ enum netlogon_creds_cli_lck_type type,
+ TALLOC_CTX *mem_ctx, struct netlogon_creds_cli_lck **lck);
+
+struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ uint8_t num_nt_hashes,
+ const struct samr_Password * const *nt_hashes);
+NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req,
+ uint8_t *idx_nt_hashes);
+NTSTATUS netlogon_creds_cli_auth(struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ uint8_t num_nt_hashes,
+ const struct samr_Password * const *nt_hashes,
+ uint8_t *idx_nt_hashes);
+
+struct tevent_req *netlogon_creds_cli_check_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b);
+NTSTATUS netlogon_creds_cli_check_recv(struct tevent_req *req,
+ union netr_Capabilities *capabilities);
+NTSTATUS netlogon_creds_cli_check(struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ union netr_Capabilities *capabilities);
+
+struct tevent_req *netlogon_creds_cli_ServerPasswordSet_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ const DATA_BLOB *new_password,
+ const uint32_t *new_version);
+NTSTATUS netlogon_creds_cli_ServerPasswordSet_recv(struct tevent_req *req);
+NTSTATUS netlogon_creds_cli_ServerPasswordSet(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ const DATA_BLOB *new_password,
+ const uint32_t *new_version);
+
+struct tevent_req *netlogon_creds_cli_LogonSamLogon_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ enum netr_LogonInfoClass logon_level,
+ const union netr_LogonLevel *logon,
+ uint32_t flags);
+NTSTATUS netlogon_creds_cli_LogonSamLogon_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint16_t *validation_level,
+ union netr_Validation **validation,
+ uint8_t *authoritative,
+ uint32_t *flags);
+NTSTATUS netlogon_creds_cli_LogonSamLogon(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ enum netr_LogonInfoClass logon_level,
+ const union netr_LogonLevel *logon,
+ TALLOC_CTX *mem_ctx,
+ uint16_t *validation_level,
+ union netr_Validation **validation,
+ uint8_t *authoritative,
+ uint32_t *flags);
+struct tevent_req *netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ const char *site_name,
+ uint32_t dns_ttl,
+ struct NL_DNS_NAME_INFO_ARRAY *dns_names);
+NTSTATUS netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_recv(struct tevent_req *req);
+NTSTATUS netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ const char *site_name,
+ uint32_t dns_ttl,
+ struct NL_DNS_NAME_INFO_ARRAY *dns_names);
+
+struct tevent_req *netlogon_creds_cli_ServerGetTrustInfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b);
+NTSTATUS netlogon_creds_cli_ServerGetTrustInfo_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct samr_Password *new_owf_password,
+ struct samr_Password *old_owf_password,
+ struct netr_TrustInfo **trust_info);
+NTSTATUS netlogon_creds_cli_ServerGetTrustInfo(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ struct samr_Password *new_owf_password,
+ struct samr_Password *old_owf_password,
+ struct netr_TrustInfo **trust_info);
+
+struct tevent_req *netlogon_creds_cli_GetForestTrustInformation_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b);
+NTSTATUS netlogon_creds_cli_GetForestTrustInformation_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_ForestTrustInformation **forest_trust_info);
+NTSTATUS netlogon_creds_cli_GetForestTrustInformation(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_ForestTrustInformation **forest_trust_info);
+
+struct tevent_req *netlogon_creds_cli_SendToSam_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ struct netr_SendToSamBase *message);
+NTSTATUS netlogon_creds_cli_SendToSam_recv(struct tevent_req *req);
+NTSTATUS netlogon_creds_cli_SendToSam(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ struct netr_SendToSamBase *message);
+
+struct tevent_req *netlogon_creds_cli_LogonGetDomainInfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ uint32_t level,
+ union netr_WorkstationInfo *query);
+NTSTATUS netlogon_creds_cli_LogonGetDomainInfo_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ union netr_DomainInfo **info);
+NTSTATUS netlogon_creds_cli_LogonGetDomainInfo(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ uint32_t level,
+ union netr_WorkstationInfo *query,
+ union netr_DomainInfo **info);
+
+#endif /* NETLOGON_CREDS_CLI_H */
diff --git a/libcli/auth/ntlm_check.c b/libcli/auth/ntlm_check.c
new file mode 100644
index 0000000..cb4be7f
--- /dev/null
+++ b/libcli/auth/ntlm_check.c
@@ -0,0 +1,648 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
+ Copyright (C) Gerald Carter 2003
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/crypto/md4.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "libcli/auth/libcli_auth.h"
+
+/****************************************************************************
+ Core of smb password checking routine.
+****************************************************************************/
+
+static bool smb_pwd_check_ntlmv1(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *nt_response,
+ const uint8_t *part_passwd,
+ const DATA_BLOB *sec_blob,
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uint8_t p24[24];
+ int rc;
+ bool ok;
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always false ! */
+ return false;
+ }
+
+ if (sec_blob->length != 8) {
+ DBG_ERR("incorrect challenge size (%zu)\n", sec_blob->length);
+ return false;
+ }
+
+ if (nt_response->length != 24) {
+ DBG_ERR("incorrect password length (%zu)\n",
+ nt_response->length);
+ return false;
+ }
+
+ rc = SMBOWFencrypt(part_passwd, sec_blob->data, p24);
+ if (rc != 0) {
+ return false;
+ }
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("Part password (P16) was |\n"));
+ dump_data(100, part_passwd, 16);
+ DEBUGADD(100,("Password from client was |\n"));
+ dump_data(100, nt_response->data, nt_response->length);
+ DEBUGADD(100,("Given challenge was |\n"));
+ dump_data(100, sec_blob->data, sec_blob->length);
+ DEBUGADD(100,("Value from encryption was |\n"));
+ dump_data(100, p24, 24);
+#endif
+ ok = mem_equal_const_time(p24, nt_response->data, 24);
+ if (!ok) {
+ return false;
+ }
+ if (user_sess_key != NULL) {
+ *user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
+ if (user_sess_key->data == NULL) {
+ DBG_ERR("data_blob_talloc failed\n");
+ return false;
+ }
+ SMBsesskeygen_ntv1(part_passwd, user_sess_key->data);
+ }
+ return true;
+}
+
+/****************************************************************************
+ Core of smb password checking routine. (NTLMv2, LMv2)
+ Note: The same code works with both NTLMv2 and LMv2.
+****************************************************************************/
+
+static bool smb_pwd_check_ntlmv2(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *ntv2_response,
+ const uint8_t *part_passwd,
+ const DATA_BLOB *sec_blob,
+ const char *user, const char *domain,
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uint8_t kr[16];
+ uint8_t value_from_encryption[16];
+ DATA_BLOB client_key_data;
+ NTSTATUS status;
+ bool ok;
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always false */
+ return false;
+ }
+
+ if (sec_blob->length != 8) {
+ DBG_ERR("incorrect challenge size (%zu)\n", sec_blob->length);
+ return false;
+ }
+
+ if (ntv2_response->length < 24) {
+ /* We MUST have more than 16 bytes, or the stuff below will go
+ crazy. No known implementation sends less than the 24 bytes
+ for LMv2, let alone NTLMv2. */
+ DBG_ERR("incorrect password length (%zu)\n",
+ ntv2_response->length);
+ return false;
+ }
+
+ client_key_data = data_blob_talloc(mem_ctx, ntv2_response->data+16, ntv2_response->length-16);
+ /*
+ todo: should we be checking this for anything? We can't for LMv2,
+ but for NTLMv2 it is meant to contain the current time etc.
+ */
+
+ if (!ntv2_owf_gen(part_passwd, user, domain, kr)) {
+ return false;
+ }
+
+ status = SMBOWFencrypt_ntv2(kr,
+ sec_blob,
+ &client_key_data,
+ value_from_encryption);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("Part password (P16) was |\n"));
+ dump_data(100, part_passwd, 16);
+ DEBUGADD(100,("Password from client was |\n"));
+ dump_data(100, ntv2_response->data, ntv2_response->length);
+ DEBUGADD(100,("Variable data from client was |\n"));
+ dump_data(100, client_key_data.data, client_key_data.length);
+ DEBUGADD(100,("Given challenge was |\n"));
+ dump_data(100, sec_blob->data, sec_blob->length);
+ DEBUGADD(100,("Value from encryption was |\n"));
+ dump_data(100, value_from_encryption, 16);
+#endif
+ data_blob_clear_free(&client_key_data);
+
+ ok = mem_equal_const_time(value_from_encryption, ntv2_response->data, 16);
+ if (!ok) {
+ return false;
+ }
+ if (user_sess_key != NULL) {
+ *user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
+ if (user_sess_key->data == NULL) {
+ DBG_ERR("data_blob_talloc failed\n");
+ return false;
+ }
+
+ status = SMBsesskeygen_ntv2(
+ kr, value_from_encryption, user_sess_key->data);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/****************************************************************************
+ Core of smb password checking routine. (NTLMv2, LMv2)
+ Note: The same code works with both NTLMv2 and LMv2.
+****************************************************************************/
+
+static bool smb_sess_key_ntlmv2(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *ntv2_response,
+ const uint8_t *part_passwd,
+ const DATA_BLOB *sec_blob,
+ const char *user, const char *domain,
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uint8_t kr[16];
+ uint8_t value_from_encryption[16];
+ DATA_BLOB client_key_data;
+ NTSTATUS status;
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always false */
+ return false;
+ }
+
+ if (sec_blob->length != 8) {
+ DBG_ERR("incorrect challenge size (%zu)\n", sec_blob->length);
+ return false;
+ }
+
+ if (ntv2_response->length < 24) {
+ /* We MUST have more than 16 bytes, or the stuff below will go
+ crazy. No known implementation sends less than the 24 bytes
+ for LMv2, let alone NTLMv2. */
+ DBG_ERR("incorrect password length (%zu)\n",
+ ntv2_response->length);
+ return false;
+ }
+
+ client_key_data = data_blob_talloc(mem_ctx, ntv2_response->data+16, ntv2_response->length-16);
+
+ if (!ntv2_owf_gen(part_passwd, user, domain, kr)) {
+ return false;
+ }
+
+ status = SMBOWFencrypt_ntv2(kr,
+ sec_blob,
+ &client_key_data,
+ value_from_encryption);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ *user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
+ if (user_sess_key->data == NULL) {
+ DBG_ERR("data_blob_talloc failed\n");
+ return false;
+ }
+ status = SMBsesskeygen_ntv2(kr,
+ value_from_encryption,
+ user_sess_key->data);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Compare password hashes against those from the SAM
+ *
+ * @param mem_ctx talloc context
+ * @param client_lanman LANMAN password hash, as supplied by the client
+ * @param client_nt NT (MD4) password hash, as supplied by the client
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param stored_lanman LANMAN password hash, as stored on the SAM
+ * @param stored_nt NT (MD4) password hash, as stored on the SAM
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS hash_password_check(TALLOC_CTX *mem_ctx,
+ bool lanman_auth,
+ const struct samr_Password *client_lanman,
+ const struct samr_Password *client_nt,
+ const char *username,
+ const struct samr_Password *stored_lanman,
+ const struct samr_Password *stored_nt)
+{
+ if (stored_nt == NULL) {
+ DEBUG(3,("hash_password_check: NO NT password stored for user %s.\n",
+ username));
+ }
+
+ if (client_nt && stored_nt) {
+ if (mem_equal_const_time(client_nt->hash, stored_nt->hash, sizeof(stored_nt->hash))) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("hash_password_check: Interactive logon: NT password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ } else if (client_lanman && stored_lanman) {
+ if (!lanman_auth) {
+ DEBUG(3,("hash_password_check: Interactive logon: only LANMAN password supplied for user %s, and LM passwords are disabled!\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ if (strchr_m(username, '@')) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ if (mem_equal_const_time(client_lanman->hash, stored_lanman->hash, sizeof(stored_lanman->hash))) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("hash_password_check: Interactive logon: LANMAN password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+ if (strchr_m(username, '@')) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
+/**
+ * Check a challenge-response password against the value of the NT or
+ * LM password hash.
+ *
+ * @param mem_ctx talloc context
+ * @param challenge 8-byte challenge. If all zero, forces plaintext comparison
+ * @param nt_response 'unicode' NT response to the challenge, or unicode password
+ * @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param stored_lanman LANMAN ASCII password from our passdb or similar
+ * @param stored_nt MD4 unicode password from our passdb or similar
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx,
+ bool lanman_auth,
+ enum ntlm_auth_level ntlm_auth,
+ uint32_t logon_parameters,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ const char *username,
+ const char *client_username,
+ const char *client_domain,
+ const struct samr_Password *stored_lanman,
+ const struct samr_Password *stored_nt,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key)
+{
+ DATA_BLOB tmp_sess_key;
+ const char *upper_client_domain = NULL;
+
+ if (ntlm_auth == NTLM_AUTH_DISABLED) {
+ DBG_WARNING("ntlm_password_check: NTLM authentication not "
+ "permitted by configuration.\n");
+ return NT_STATUS_NTLM_BLOCKED;
+ }
+
+ if (client_domain != NULL) {
+ upper_client_domain = talloc_strdup_upper(mem_ctx, client_domain);
+ if (upper_client_domain == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (stored_nt == NULL) {
+ DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
+ username));
+ }
+
+ *lm_sess_key = data_blob(NULL, 0);
+ *user_sess_key = data_blob(NULL, 0);
+
+ /* Check for cleartext netlogon. Used by Exchange 5.5. */
+ if ((logon_parameters & MSV1_0_CLEARTEXT_PASSWORD_ALLOWED)
+ && challenge->length == 8
+ && (all_zero(challenge->data, challenge->length))) {
+ struct samr_Password client_nt;
+ struct samr_Password client_lm;
+ char *unix_pw = NULL;
+ bool lm_ok;
+ size_t converted_size = 0;
+
+ DEBUG(4,("ntlm_password_check: checking plaintext passwords for user %s\n",
+ username));
+ mdfour(client_nt.hash, nt_response->data, nt_response->length);
+
+ if (lm_response->length &&
+ (convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX,
+ lm_response->data, lm_response->length,
+ (void *)&unix_pw, &converted_size))) {
+ if (E_deshash(unix_pw, client_lm.hash)) {
+ lm_ok = true;
+ } else {
+ lm_ok = false;
+ }
+ } else {
+ lm_ok = false;
+ }
+ return hash_password_check(mem_ctx,
+ lanman_auth,
+ lm_ok ? &client_lm : NULL,
+ nt_response->length ? &client_nt : NULL,
+ username,
+ stored_lanman, stored_nt);
+ }
+
+ if (nt_response->length != 0 && nt_response->length < 24) {
+ DBG_NOTICE("invalid NT password length (%zu) for user %s\n",
+ nt_response->length,
+ username);
+ }
+
+ if (nt_response->length > 24 && stored_nt) {
+ /* We have the NT MD4 hash challenge available - see if we can
+ use it
+ */
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with domain [%s]\n",
+ client_domain ? client_domain : "<NULL>"));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ user_sess_key)) {
+ if (user_sess_key->length) {
+ *lm_sess_key = data_blob_talloc(mem_ctx, user_sess_key->data, MIN(8, user_sess_key->length));
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with uppercased version of domain [%s]\n",
+ upper_client_domain ? upper_client_domain : "<NULL>"));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ upper_client_domain,
+ user_sess_key)) {
+ if (user_sess_key->length) {
+ *lm_sess_key = data_blob_talloc(mem_ctx, user_sess_key->data, MIN(8, user_sess_key->length));
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password without a domain\n"));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ "",
+ user_sess_key)) {
+ if (user_sess_key->length) {
+ *lm_sess_key = data_blob_talloc(mem_ctx, user_sess_key->data, MIN(8, user_sess_key->length));
+ }
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NTLMv2 password check failed\n"));
+ }
+ } else if (nt_response->length == 24 && stored_nt) {
+ if (ntlm_auth == NTLM_AUTH_ON
+ || (ntlm_auth == NTLM_AUTH_MSCHAPv2_NTLMV2_ONLY && (logon_parameters & MSV1_0_ALLOW_MSVCHAPV2))) {
+ /* We have the NT MD4 hash challenge available - see if we can
+ use it (ie. does it exist in the smbpasswd file).
+ */
+ DEBUG(4,("ntlm_password_check: Checking NT MD4 password\n"));
+ if (smb_pwd_check_ntlmv1(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ user_sess_key)) {
+ /* The LM session key for this response is not very secure,
+ so use it only if we otherwise allow LM authentication */
+
+ if (lanman_auth && stored_lanman) {
+ *lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, MIN(8, user_sess_key->length));
+ }
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NT MD4 password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ } else {
+ DEBUG(2,("ntlm_password_check: NTLMv1 passwords NOT PERMITTED for user %s\n",
+ username));
+ /* no return, because we might pick up LMv2 in the LM field */
+ }
+ }
+
+ if (lm_response->length == 0) {
+ DEBUG(3,("ntlm_password_check: NEITHER LanMan nor NT password supplied for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (lm_response->length < 24) {
+ DBG_NOTICE("invalid LanMan password length (%zu) for "
+ "user %s\n",
+ nt_response->length, username);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (!lanman_auth) {
+ DEBUG(3,("ntlm_password_check: Lanman passwords NOT PERMITTED for user %s\n",
+ username));
+ } else if (!stored_lanman) {
+ DEBUG(3,("ntlm_password_check: NO LanMan password set for user %s (and no NT password supplied)\n",
+ username));
+ } else if (strchr_m(username, '@')) {
+ DEBUG(3,("ntlm_password_check: NO LanMan password allowed for username@realm logins (user: %s)\n",
+ username));
+ } else {
+ DEBUG(4,("ntlm_password_check: Checking LM password\n"));
+ if (smb_pwd_check_ntlmv1(mem_ctx,
+ lm_response,
+ stored_lanman->hash, challenge,
+ NULL)) {
+ /* The session key for this response is still very odd.
+ It not very secure, so use it only if we otherwise
+ allow LM authentication */
+
+ if (lanman_auth && stored_lanman) {
+ uint8_t first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, stored_lanman->hash, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ *user_sess_key = data_blob_talloc(mem_ctx, first_8_lm_hash, 16);
+ *lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
+ }
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (!stored_nt) {
+ DEBUG(4,("ntlm_password_check: LM password check failed for user, no NT password %s\n",username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* This is for 'LMv2' authentication. almost NTLMv2 but limited to 24 bytes.
+ - related to Win9X, legacy NAS pass-though authentication
+ */
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password with domain %s\n",
+ client_domain ? client_domain : "<NULL>"));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ &tmp_sess_key)) {
+ if (nt_response->length > 24) {
+ /* If NTLMv2 authentication has preceded us
+ * (even if it failed), then use the session
+ * key from that. See the RPC-SAMLOGON
+ * torture test */
+ smb_sess_key_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ user_sess_key);
+ } else {
+ /* Otherwise, use the LMv2 session key */
+ *user_sess_key = tmp_sess_key;
+ }
+ if (user_sess_key->length) {
+ *lm_sess_key = data_blob_talloc(mem_ctx, user_sess_key->data, MIN(8, user_sess_key->length));
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password with upper-cased version of domain %s\n",
+ upper_client_domain ? upper_client_domain : "<NULL>"));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+ client_username,
+ upper_client_domain,
+ &tmp_sess_key)) {
+ if (nt_response->length > 24) {
+ /* If NTLMv2 authentication has preceded us
+ * (even if it failed), then use the session
+ * key from that. See the RPC-SAMLOGON
+ * torture test */
+ smb_sess_key_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ upper_client_domain,
+ user_sess_key);
+ } else {
+ /* Otherwise, use the LMv2 session key */
+ *user_sess_key = tmp_sess_key;
+ }
+ if (user_sess_key->length) {
+ *lm_sess_key = data_blob_talloc(mem_ctx, user_sess_key->data, MIN(8, user_sess_key->length));
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password without a domain\n"));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+ client_username,
+ "",
+ &tmp_sess_key)) {
+ if (nt_response->length > 24) {
+ /* If NTLMv2 authentication has preceded us
+ * (even if it failed), then use the session
+ * key from that. See the RPC-SAMLOGON
+ * torture test */
+ smb_sess_key_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ "",
+ user_sess_key);
+ } else {
+ /* Otherwise, use the LMv2 session key */
+ *user_sess_key = tmp_sess_key;
+ }
+ if (user_sess_key->length) {
+ *lm_sess_key = data_blob_talloc(mem_ctx, user_sess_key->data, MIN(8, user_sess_key->length));
+ }
+ return NT_STATUS_OK;
+ }
+
+ /* Apparently NT accepts NT responses in the LM field
+ - I think this is related to Win9X pass-though authentication
+ */
+ DEBUG(4,("ntlm_password_check: Checking NT MD4 password in LM field\n"));
+ if (ntlm_auth == NTLM_AUTH_ON) {
+ if (smb_pwd_check_ntlmv1(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+ NULL)) {
+ /* The session key for this response is still very odd.
+ It not very secure, so use it only if we otherwise
+ allow LM authentication */
+
+ if (lanman_auth && stored_lanman) {
+ uint8_t first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, stored_lanman->hash, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ *user_sess_key = data_blob_talloc(mem_ctx, first_8_lm_hash, 16);
+ *lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
+ }
+ return NT_STATUS_OK;
+ }
+ DEBUG(3,("ntlm_password_check: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",username));
+ } else {
+ DEBUG(3,("ntlm_password_check: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",username));
+ }
+
+ /* Try and match error codes */
+ if (strchr_m(username, '@')) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
diff --git a/libcli/auth/ntlm_check.h b/libcli/auth/ntlm_check.h
new file mode 100644
index 0000000..86cab9b
--- /dev/null
+++ b/libcli/auth/ntlm_check.h
@@ -0,0 +1,86 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
+ Copyright (C) Gerald Carter 2003
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __LIBCLI_AUTH_NTLM_CHECK_H__
+#define __LIBCLI_AUTH_NTLM_CHECK_H__
+
+/* mangled names options */
+enum ntlm_auth_level {NTLM_AUTH_DISABLED, NTLM_AUTH_ON,
+ NTLM_AUTH_NTLMV2_ONLY,
+ NTLM_AUTH_MSCHAPv2_NTLMV2_ONLY};
+
+struct samr_Password;
+
+/**
+ * Compare password hashes against those from the SAM
+ *
+ * @param mem_ctx talloc context
+ * @param client_lanman LANMAN password hash, as supplied by the client
+ * @param client_nt NT (MD4) password hash, as supplied by the client
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param stored_lanman LANMAN password hash, as stored on the SAM
+ * @param stored_nt NT (MD4) password hash, as stored on the SAM
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS hash_password_check(TALLOC_CTX *mem_ctx,
+ bool lanman_auth,
+ const struct samr_Password *client_lanman,
+ const struct samr_Password *client_nt,
+ const char *username,
+ const struct samr_Password *stored_lanman,
+ const struct samr_Password *stored_nt);
+
+/**
+ * Check a challenge-response password against the value of the NT or
+ * LM password hash.
+ *
+ * @param mem_ctx talloc context
+ * @param challenge 8-byte challenge. If all zero, forces plaintext comparison
+ * @param nt_response 'unicode' NT response to the challenge, or unicode password
+ * @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param stored_lanman LANMAN ASCII password from our passdb or similar
+ * @param stored_nt MD4 unicode password from our passdb or similar
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx,
+ bool lanman_auth,
+ enum ntlm_auth_level ntlm_auth,
+ uint32_t logon_parameters,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ const char *username,
+ const char *client_username,
+ const char *client_domain,
+ const struct samr_Password *stored_lanman,
+ const struct samr_Password *stored_nt,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key);
+
+#endif /* __LIBCLI_AUTH_NTLM_CHECK_H__ */
diff --git a/libcli/auth/pam_errors.c b/libcli/auth/pam_errors.c
new file mode 100644
index 0000000..5592d39
--- /dev/null
+++ b/libcli/auth/pam_errors.c
@@ -0,0 +1,143 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * PAM error mapping functions
+ * Copyright (C) Andrew Bartlett 2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "../libcli/auth/pam_errors.h"
+
+#ifdef WITH_PAM
+#if defined(HAVE_SECURITY_PAM_APPL_H)
+#include <security/pam_appl.h>
+#elif defined(HAVE_PAM_PAM_APPL_H)
+#include <pam/pam_appl.h>
+#endif
+
+#if defined(PAM_AUTHTOK_RECOVERY_ERR) && !defined(PAM_AUTHTOK_RECOVER_ERR)
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+/* PAM -> NT_STATUS map */
+static const struct {
+ int pam_code;
+ NTSTATUS ntstatus;
+} pam_to_nt_status_map[] = {
+ {PAM_OPEN_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_SYMBOL_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_SERVICE_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_SYSTEM_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_BUF_ERR, NT_STATUS_NO_MEMORY},
+ {PAM_PERM_DENIED, NT_STATUS_ACCESS_DENIED},
+ {PAM_AUTH_ERR, NT_STATUS_WRONG_PASSWORD},
+ {PAM_CRED_INSUFFICIENT, NT_STATUS_INSUFFICIENT_LOGON_INFO}, /* FIXME: Is this correct? */
+ {PAM_AUTHINFO_UNAVAIL, NT_STATUS_LOGON_FAILURE},
+ {PAM_USER_UNKNOWN, NT_STATUS_NO_SUCH_USER},
+ {PAM_MAXTRIES, NT_STATUS_REMOTE_SESSION_LIMIT}, /* FIXME: Is this correct? */
+ {PAM_NEW_AUTHTOK_REQD, NT_STATUS_PASSWORD_MUST_CHANGE},
+ {PAM_ACCT_EXPIRED, NT_STATUS_ACCOUNT_EXPIRED},
+ {PAM_SESSION_ERR, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {PAM_CRED_UNAVAIL, NT_STATUS_NO_TOKEN}, /* FIXME: Is this correct? */
+ {PAM_CRED_EXPIRED, NT_STATUS_PASSWORD_EXPIRED}, /* FIXME: Is this correct? */
+ {PAM_CRED_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_AUTHTOK_ERR, NT_STATUS_UNSUCCESSFUL},
+#ifdef PAM_AUTHTOK_RECOVER_ERR
+ {PAM_AUTHTOK_RECOVER_ERR, NT_STATUS_UNSUCCESSFUL},
+#endif
+ {PAM_AUTHTOK_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},
+ {PAM_SUCCESS, NT_STATUS_OK}
+};
+
+/* NT_STATUS -> PAM map */
+static const struct {
+ NTSTATUS ntstatus;
+ int pam_code;
+} nt_status_to_pam_map[] = {
+ {NT_STATUS_UNSUCCESSFUL, PAM_SYSTEM_ERR},
+ {NT_STATUS_NO_SUCH_USER, PAM_USER_UNKNOWN},
+ {NT_STATUS_WRONG_PASSWORD, PAM_AUTH_ERR},
+ {NT_STATUS_LOGON_FAILURE, PAM_AUTH_ERR},
+ {NT_STATUS_ACCOUNT_EXPIRED, PAM_ACCT_EXPIRED},
+ {NT_STATUS_ACCOUNT_DISABLED, PAM_ACCT_EXPIRED},
+ {NT_STATUS_PASSWORD_EXPIRED, PAM_AUTHTOK_EXPIRED},
+ {NT_STATUS_PASSWORD_MUST_CHANGE, PAM_NEW_AUTHTOK_REQD},
+ {NT_STATUS_ACCOUNT_LOCKED_OUT, PAM_MAXTRIES},
+ {NT_STATUS_NO_MEMORY, PAM_BUF_ERR},
+ {NT_STATUS_PASSWORD_RESTRICTION, PAM_AUTHTOK_ERR},
+ {NT_STATUS_PWD_HISTORY_CONFLICT, PAM_AUTHTOK_ERR},
+ {NT_STATUS_PWD_TOO_RECENT, PAM_AUTHTOK_ERR},
+ {NT_STATUS_PWD_TOO_SHORT, PAM_AUTHTOK_ERR},
+ {NT_STATUS_BACKUP_CONTROLLER, PAM_AUTHINFO_UNAVAIL},
+ {NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND, PAM_AUTHINFO_UNAVAIL},
+ {NT_STATUS_NO_LOGON_SERVERS, PAM_AUTHINFO_UNAVAIL},
+ {NT_STATUS_INVALID_WORKSTATION, PAM_PERM_DENIED},
+ {NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, PAM_AUTHINFO_UNAVAIL},
+ {NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT, PAM_AUTHINFO_UNAVAIL},
+ {NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, PAM_AUTHINFO_UNAVAIL},
+ {NT_STATUS_OK, PAM_SUCCESS}
+};
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error)
+{
+ int i;
+ if (pam_error == 0) return NT_STATUS_OK;
+
+ for (i=0; NT_STATUS_V(pam_to_nt_status_map[i].ntstatus); i++) {
+ if (pam_error == pam_to_nt_status_map[i].pam_code)
+ return pam_to_nt_status_map[i].ntstatus;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status)
+{
+ int i;
+ if NT_STATUS_IS_OK(nt_status) return PAM_SUCCESS;
+
+ for (i=0; NT_STATUS_V(nt_status_to_pam_map[i].ntstatus); i++) {
+ if (NT_STATUS_EQUAL(nt_status,nt_status_to_pam_map[i].ntstatus))
+ return nt_status_to_pam_map[i].pam_code;
+ }
+ return PAM_SYSTEM_ERR;
+}
+
+#else
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error)
+{
+ if (pam_error == 0) return NT_STATUS_OK;
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status)
+{
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OK)) return 0;
+ return 4; /* PAM_SYSTEM_ERR */
+}
+
+#endif
diff --git a/libcli/auth/pam_errors.h b/libcli/auth/pam_errors.h
new file mode 100644
index 0000000..128910f
--- /dev/null
+++ b/libcli/auth/pam_errors.h
@@ -0,0 +1,33 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * PAM error mapping functions
+ * Copyright (C) Andrew Bartlett 2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBCLI_AUTH_PAM_ERRORS_H__
+#define __LIBCLI_AUTH_PAM_ERRORS_H__
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error);
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status);
+
+#endif /* __LIBCLI_AUTH_PAM_ERRORS_H__ */
diff --git a/libcli/auth/proto.h b/libcli/auth/proto.h
new file mode 100644
index 0000000..baf5730
--- /dev/null
+++ b/libcli/auth/proto.h
@@ -0,0 +1,296 @@
+#ifndef _LIBCLI_AUTH_PROTO_H__
+#define _LIBCLI_AUTH_PROTO_H__
+
+#undef _PRINTF_ATTRIBUTE
+#define _PRINTF_ATTRIBUTE(a1, a2) PRINTF_ATTRIBUTE(a1, a2)
+
+#include "lib/crypto/gnutls_helpers.h"
+
+/* this file contains prototypes for functions that are private
+ * to this subsystem or library. These functions should not be
+ * used outside this particular subsystem! */
+
+
+/* The following definitions come from /home/jeremy/src/samba/git/master/source3/../source4/../libcli/auth/credentials.c */
+
+bool netlogon_creds_is_random_challenge(const struct netr_Credential *challenge);
+void netlogon_creds_random_challenge(struct netr_Credential *challenge);
+
+NTSTATUS netlogon_creds_des_encrypt_LMKey(struct netlogon_creds_CredentialState *creds,
+ struct netr_LMSessionKey *key);
+NTSTATUS netlogon_creds_des_decrypt_LMKey(struct netlogon_creds_CredentialState *creds,
+ struct netr_LMSessionKey *key);
+NTSTATUS netlogon_creds_des_encrypt(struct netlogon_creds_CredentialState *creds,
+ struct samr_Password *pass);
+NTSTATUS netlogon_creds_des_decrypt(struct netlogon_creds_CredentialState *creds,
+ struct samr_Password *pass);
+NTSTATUS netlogon_creds_arcfour_crypt(struct netlogon_creds_CredentialState *creds,
+ uint8_t *data,
+ size_t len);
+NTSTATUS netlogon_creds_aes_encrypt(struct netlogon_creds_CredentialState *creds,
+ uint8_t *data,
+ size_t len);
+NTSTATUS netlogon_creds_aes_decrypt(struct netlogon_creds_CredentialState *creds,
+ uint8_t *data,
+ size_t len);
+
+/*****************************************************************
+The above functions are common to the client and server interface
+next comes the client specific functions
+******************************************************************/
+struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *mem_ctx,
+ const char *client_account,
+ const char *client_computer_name,
+ uint16_t secure_channel_type,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const struct samr_Password *machine_password,
+ struct netr_Credential *initial_credential,
+ uint32_t negotiate_flags);
+struct netlogon_creds_CredentialState *netlogon_creds_client_init_session_key(TALLOC_CTX *mem_ctx,
+ const uint8_t session_key[16]);
+NTSTATUS
+netlogon_creds_client_authenticator(struct netlogon_creds_CredentialState *creds,
+ struct netr_Authenticator *next);
+bool netlogon_creds_client_check(struct netlogon_creds_CredentialState *creds,
+ const struct netr_Credential *received_credentials);
+struct netlogon_creds_CredentialState *netlogon_creds_copy(
+ TALLOC_CTX *mem_ctx,
+ const struct netlogon_creds_CredentialState *creds_in);
+
+/*****************************************************************
+The above functions are common to the client and server interface
+next comes the server specific functions
+******************************************************************/
+struct netlogon_creds_CredentialState *netlogon_creds_server_init(TALLOC_CTX *mem_ctx,
+ const char *client_account,
+ const char *client_computer_name,
+ uint16_t secure_channel_type,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const struct samr_Password *machine_password,
+ const struct netr_Credential *credentials_in,
+ struct netr_Credential *credentials_out,
+ uint32_t negotiate_flags);
+NTSTATUS netlogon_creds_server_step_check(struct netlogon_creds_CredentialState *creds,
+ const struct netr_Authenticator *received_authenticator,
+ struct netr_Authenticator *return_authenticator) ;
+NTSTATUS netlogon_creds_decrypt_samlogon_validation(struct netlogon_creds_CredentialState *creds,
+ uint16_t validation_level,
+ union netr_Validation *validation);
+NTSTATUS netlogon_creds_encrypt_samlogon_validation(struct netlogon_creds_CredentialState *creds,
+ uint16_t validation_level,
+ union netr_Validation *validation);
+NTSTATUS netlogon_creds_decrypt_samlogon_logon(struct netlogon_creds_CredentialState *creds,
+ enum netr_LogonInfoClass level,
+ union netr_LogonLevel *logon);
+NTSTATUS netlogon_creds_encrypt_samlogon_logon(struct netlogon_creds_CredentialState *creds,
+ enum netr_LogonInfoClass level,
+ union netr_LogonLevel *logon);
+union netr_LogonLevel *netlogon_creds_shallow_copy_logon(TALLOC_CTX *mem_ctx,
+ enum netr_LogonInfoClass level,
+ const union netr_LogonLevel *in);
+
+/* The following definitions come from /home/jeremy/src/samba/git/master/source3/../source4/../libcli/auth/session.c */
+
+int sess_crypt_blob(DATA_BLOB *out, const DATA_BLOB *in, const DATA_BLOB *session_key,
+ enum samba_gnutls_direction encrypt);
+DATA_BLOB sess_encrypt_string(const char *str, const DATA_BLOB *session_key);
+char *sess_decrypt_string(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob, const DATA_BLOB *session_key);
+DATA_BLOB sess_encrypt_blob(TALLOC_CTX *mem_ctx, DATA_BLOB *blob_in, const DATA_BLOB *session_key);
+NTSTATUS sess_decrypt_blob(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, const DATA_BLOB *session_key,
+ DATA_BLOB *ret);
+
+/* The following definitions come from /home/jeremy/src/samba/git/master/source3/../source4/../libcli/auth/smbencrypt.c */
+
+int SMBencrypt_hash(const uint8_t lm_hash[16], const uint8_t *c8, uint8_t p24[24]);
+bool SMBencrypt(const char *passwd, const uint8_t *c8, uint8_t p24[24]);
+
+/**
+ * Creates the MD4 Hash of the users password in NT UNICODE.
+ * @param passwd password in 'unix' charset.
+ * @param p16 return password hashed with md4, caller allocated 16 byte buffer
+ */
+bool E_md4hash(const char *passwd, uint8_t p16[16]);
+
+/**
+ * Creates the DES forward-only Hash of the users password in DOS ASCII charset
+ * @param passwd password in 'unix' charset.
+ * @param p16 return password hashed with DES, caller allocated 16 byte buffer
+ * @return false if password was > 14 characters, and therefore may be incorrect, otherwise true
+ * @note p16 is filled in regardless
+ */
+bool E_deshash(const char *passwd, uint8_t p16[16]);
+
+/**
+ * Creates the MD4 and DES (LM) Hash of the users password.
+ * MD4 is of the NT Unicode, DES is of the DOS UPPERCASE password.
+ * @param passwd password in 'unix' charset.
+ * @param nt_p16 return password hashed with md4, caller allocated 16 byte buffer
+ * @param p16 return password hashed with des, caller allocated 16 byte buffer
+ */
+void nt_lm_owf_gen(const char *pwd, uint8_t nt_p16[16], uint8_t p16[16]);
+bool ntv2_owf_gen(const uint8_t owf[16],
+ const char *user_in, const char *domain_in,
+ uint8_t kr_buf[16]);
+int SMBOWFencrypt(const uint8_t passwd[16], const uint8_t *c8, uint8_t p24[24]);
+int SMBNTencrypt_hash(const uint8_t nt_hash[16], const uint8_t *c8, uint8_t *p24);
+int SMBNTencrypt(const char *passwd, const uint8_t *c8, uint8_t *p24);
+NTSTATUS SMBOWFencrypt_ntv2(const uint8_t kr[16],
+ const DATA_BLOB *srv_chal,
+ const DATA_BLOB *smbcli_chal,
+ uint8_t resp_buf[16]);
+NTSTATUS SMBsesskeygen_ntv2(const uint8_t kr[16],
+ const uint8_t *nt_resp,
+ uint8_t sess_key[16]);
+void SMBsesskeygen_ntv1(const uint8_t kr[16], uint8_t sess_key[16]);
+NTSTATUS SMBsesskeygen_lm_sess_key(const uint8_t lm_hash[16],
+ const uint8_t lm_resp[24], /* only uses 8 */
+ uint8_t sess_key[16]);
+DATA_BLOB NTLMv2_generate_names_blob(TALLOC_CTX *mem_ctx,
+ const char *hostname,
+ const char *domain);
+bool SMBNTLMv2encrypt_hash(TALLOC_CTX *mem_ctx,
+ const char *user, const char *domain, const uint8_t nt_hash[16],
+ const DATA_BLOB *server_chal,
+ const NTTIME *server_timestamp,
+ const DATA_BLOB *names_blob,
+ DATA_BLOB *lm_response, DATA_BLOB *nt_response,
+ DATA_BLOB *lm_session_key, DATA_BLOB *user_session_key) ;
+bool SMBNTLMv2encrypt(TALLOC_CTX *mem_ctx,
+ const char *user, const char *domain,
+ const char *password,
+ const DATA_BLOB *server_chal,
+ const DATA_BLOB *names_blob,
+ DATA_BLOB *lm_response, DATA_BLOB *nt_response,
+ DATA_BLOB *lm_session_key, DATA_BLOB *user_session_key) ;
+NTSTATUS NTLMv2_RESPONSE_verify_netlogon_creds(const char *account_name,
+ const char *account_domain,
+ const DATA_BLOB response,
+ const struct netlogon_creds_CredentialState *creds,
+ const char *workgroup);
+
+/***********************************************************
+ encode a password buffer with a unicode password. The buffer
+ is filled with random data to make it harder to attack.
+************************************************************/
+bool encode_pw_buffer(uint8_t buffer[516], const char *password, int string_flags);
+
+/***********************************************************
+ decode a password buffer
+ *new_pw_len is the length in bytes of the possibly mulitbyte
+ returned password including termination.
+************************************************************/
+bool decode_pw_buffer(TALLOC_CTX *ctx,
+ uint8_t in_buffer[516],
+ char **pp_new_pwrd,
+ size_t *new_pw_len,
+ charset_t string_charset);
+
+/**
+ * @brief Encode an password buffer before we encrypt it.
+ *
+ * @param buffer[514] The buffer to encode into.
+ *
+ * @param password The password we want to encode into the buffer.
+ *
+ * @param string_flags String flags for encoding (e.g. STR_UNICODE).
+ *
+ * @return true on success, false otherwise.
+ */
+bool encode_pwd_buffer514_from_str(uint8_t buffer[514],
+ const char *password,
+ uint32_t string_flags);
+
+/**
+ * @brief Extract AES password blob from buffer.
+ *
+ * This extracts the password from the in_buffer as a data blob. It should
+ * then contain an UTF-16 encoded password.
+ *
+ * @param mem_ctx The memory context to allowcate the password on.
+ *
+ * @param in_buffer[514] The input buffer to extract the password from.
+ *
+ * @param new_password A pointer to the store the extracted password blob.
+ *
+ * @return true on success, false otherwise.
+ */
+bool extract_pwd_blob_from_buffer514(TALLOC_CTX *mem_ctx,
+ const uint8_t in_buffer[514],
+ DATA_BLOB *new_password);
+
+/**
+ * @brief Decode AES password buffer to password in the given charset.
+ *
+ * @param mem_ctx The memory context to allocate the deocded passwrod on.
+ *
+ * @param in_buffer[514] The in buffer with the decrypted password data.
+ *
+ * @param string_charset The charset to decode to.
+ *
+ * @param decoded_password A pointer to store the blob for the decoded password.
+ * It ensures that the password is NULL terminated.
+ *
+ * @return true on success, false otherwise.
+ */
+bool decode_pwd_string_from_buffer514(TALLOC_CTX *mem_ctx,
+ const uint8_t in_buffer[514],
+ charset_t string_charset,
+ DATA_BLOB *decoded_password);
+
+/***********************************************************
+ Encode an arc4 password change buffer.
+************************************************************/
+NTSTATUS encode_rc4_passwd_buffer(const char *passwd,
+ const DATA_BLOB *session_key,
+ struct samr_CryptPasswordEx *out_crypt_pwd);
+
+/***********************************************************
+ Decode an arc4 encrypted password change buffer.
+************************************************************/
+NTSTATUS decode_rc4_passwd_buffer(const DATA_BLOB *psession_key,
+ struct samr_CryptPasswordEx *inout_crypt_pwd);
+
+/***********************************************************
+ encode a password buffer with an already unicode password. The
+ rest of the buffer is filled with random data to make it harder to attack.
+************************************************************/
+bool set_pw_in_buffer(uint8_t buffer[516], const DATA_BLOB *password);
+
+/***********************************************************
+ decode a password buffer
+ *new_pw_size is the length in bytes of the extracted unicode password
+************************************************************/
+bool extract_pw_from_buffer(TALLOC_CTX *mem_ctx,
+ uint8_t in_buffer[516], DATA_BLOB *new_pass);
+struct wkssvc_PasswordBuffer;
+WERROR encode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx,
+ const char *pwd,
+ DATA_BLOB *session_key,
+ struct wkssvc_PasswordBuffer **pwd_buf);
+WERROR decode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx,
+ struct wkssvc_PasswordBuffer *pwd_buf,
+ DATA_BLOB *session_key,
+ char **pwd);
+
+/* The following definitions come from /home/jeremy/src/samba/git/master/source3/../source4/../libcli/auth/smbdes.c */
+
+int des_crypt56_gnutls(uint8_t out[8], const uint8_t in[8], const uint8_t key[7],
+ enum samba_gnutls_direction encrypt);
+int E_P16(const uint8_t *p14,uint8_t *p16);
+int E_P24(const uint8_t *p21, const uint8_t *c8, uint8_t *p24);
+int E_old_pw_hash( uint8_t *p14, const uint8_t *in, uint8_t *out);
+int des_crypt128(uint8_t out[8], const uint8_t in[8], const uint8_t key[16]);
+int des_crypt112(uint8_t out[8], const uint8_t in[8], const uint8_t key[14],
+ enum samba_gnutls_direction encrypt);
+int des_crypt112_16(uint8_t out[16], const uint8_t in[16], const uint8_t key[14],
+ enum samba_gnutls_direction encrypt);
+int sam_rid_crypt(unsigned int rid, const uint8_t *in, uint8_t *out,
+ enum samba_gnutls_direction encrypt);
+#undef _PRINTF_ATTRIBUTE
+#define _PRINTF_ATTRIBUTE(a1, a2)
+
+#endif
+
diff --git a/libcli/auth/schannel.h b/libcli/auth/schannel.h
new file mode 100644
index 0000000..c53d68e
--- /dev/null
+++ b/libcli/auth/schannel.h
@@ -0,0 +1,25 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc schannel operations
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/auth/schannel_state.h"
+#include "libcli/auth/schannel_proto.h"
diff --git a/libcli/auth/schannel_proto.h b/libcli/auth/schannel_proto.h
new file mode 100644
index 0000000..bce37c8
--- /dev/null
+++ b/libcli/auth/schannel_proto.h
@@ -0,0 +1,31 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc schannel operations
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBCLI_AUTH_SCHANNEL_PROTO_H__
+#define _LIBCLI_AUTH_SCHANNEL_PROTO_H__
+
+struct schannel_state;
+
+struct db_context *open_schannel_session_store(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx);
+
+#endif
diff --git a/libcli/auth/schannel_state.h b/libcli/auth/schannel_state.h
new file mode 100644
index 0000000..a333098
--- /dev/null
+++ b/libcli/auth/schannel_state.h
@@ -0,0 +1,54 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ module to store/fetch session keys for the schannel server
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBCLI_AUTH_SCHANNEL_STATE_H__
+#define _LIBCLI_AUTH_SCHANNEL_STATE_H__
+
+NTSTATUS schannel_get_creds_state(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *computer_name,
+ struct netlogon_creds_CredentialState **creds);
+
+NTSTATUS schannel_save_creds_state(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct netlogon_creds_CredentialState *creds);
+
+NTSTATUS schannel_check_creds_state(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *computer_name,
+ struct netr_Authenticator *received_authenticator,
+ struct netr_Authenticator *return_authenticator,
+ struct netlogon_creds_CredentialState **creds_out);
+
+NTSTATUS schannel_get_challenge(struct loadparm_context *lp_ctx,
+ struct netr_Credential *client_challenge,
+ struct netr_Credential *server_challenge,
+ const char *computer_name);
+
+NTSTATUS schannel_save_challenge(struct loadparm_context *lp_ctx,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const char *computer_name);
+
+NTSTATUS schannel_delete_challenge(struct loadparm_context *lp_ctx,
+ const char *computer_name);
+#endif
diff --git a/libcli/auth/schannel_state_tdb.c b/libcli/auth/schannel_state_tdb.c
new file mode 100644
index 0000000..e0ac8a3
--- /dev/null
+++ b/libcli/auth/schannel_state_tdb.c
@@ -0,0 +1,646 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ module to store/fetch session keys for the schannel server
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2009
+ Copyright (C) Guenther Deschner 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "../lib/tdb/include/tdb.h"
+#include "../lib/util/util_tdb.h"
+#include "../lib/param/param.h"
+#include "../libcli/auth/schannel.h"
+#include "../librpc/gen_ndr/ndr_schannel.h"
+#include "lib/dbwrap/dbwrap.h"
+
+#define SECRETS_SCHANNEL_STATE "SECRETS/SCHANNEL"
+
+/******************************************************************************
+ Open or create the schannel session store tdb. Non-static so it can
+ be called from parent processes to corectly handle TDB_CLEAR_IF_FIRST
+*******************************************************************************/
+
+struct db_context *open_schannel_session_store(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ struct db_context *db_sc = NULL;
+ char *fname = lpcfg_private_db_path(mem_ctx, lp_ctx, "schannel_store");
+ int hash_size, tdb_flags;
+
+ if (!fname) {
+ return NULL;
+ }
+
+ hash_size = lpcfg_tdb_hash_size(lp_ctx, fname);
+ tdb_flags = lpcfg_tdb_flags(lp_ctx, TDB_CLEAR_IF_FIRST|TDB_NOSYNC);
+
+ db_sc = dbwrap_local_open(
+ mem_ctx,
+ fname,
+ hash_size,
+ tdb_flags,
+ O_RDWR|O_CREAT,
+ 0600,
+ DBWRAP_LOCK_ORDER_NONE,
+ DBWRAP_FLAG_NONE);
+
+ if (!db_sc) {
+ DEBUG(0,("open_schannel_session_store: Failed to open %s - %s\n",
+ fname, strerror(errno)));
+ TALLOC_FREE(fname);
+ return NULL;
+ }
+
+ TALLOC_FREE(fname);
+
+ return db_sc;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static
+NTSTATUS schannel_store_session_key_tdb(struct db_context *db_sc,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_CredentialState *creds)
+{
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ TDB_DATA value;
+ char *keystr;
+ char *name_upper;
+ NTSTATUS status;
+
+ if (strlen(creds->computer_name) > 15) {
+ /*
+ * We may want to check for a completely
+ * valid netbios name.
+ */
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ name_upper = strupper_talloc(mem_ctx, creds->computer_name);
+ if (!name_upper) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ keystr = talloc_asprintf(mem_ctx, "%s/%s",
+ SECRETS_SCHANNEL_STATE, name_upper);
+ TALLOC_FREE(name_upper);
+ if (!keystr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, creds,
+ (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(keystr);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ value.dptr = blob.data;
+ value.dsize = blob.length;
+
+ status = dbwrap_store_bystring(db_sc, keystr, value, TDB_REPLACE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to add %s to session key db - %s\n",
+ keystr, nt_errstr(status)));
+ talloc_free(keystr);
+ return status;
+ }
+
+ DEBUG(3,("schannel_store_session_key_tdb: stored schannel info with key %s\n",
+ keystr));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
+ }
+
+ talloc_free(keystr);
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static
+NTSTATUS schannel_fetch_session_key_tdb(struct db_context *db_sc,
+ TALLOC_CTX *mem_ctx,
+ const char *computer_name,
+ struct netlogon_creds_CredentialState **pcreds)
+{
+ NTSTATUS status;
+ TDB_DATA value;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ char *keystr = NULL;
+ char *name_upper;
+
+ *pcreds = NULL;
+
+ name_upper = strupper_talloc(mem_ctx, computer_name);
+ if (!name_upper) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ keystr = talloc_asprintf(mem_ctx, "%s/%s",
+ SECRETS_SCHANNEL_STATE, name_upper);
+ TALLOC_FREE(name_upper);
+ if (!keystr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dbwrap_fetch_bystring(db_sc, keystr, keystr, &value);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("schannel_fetch_session_key_tdb: Failed to find entry with key %s\n",
+ keystr ));
+ goto done;
+ }
+
+ creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
+ if (!creds) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ blob = data_blob_const(value.dptr, value.dsize);
+
+ ndr_err = ndr_pull_struct_blob(&blob, creds, creds,
+ (ndr_pull_flags_fn_t)ndr_pull_netlogon_creds_CredentialState);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
+ }
+
+ DEBUG(3,("schannel_fetch_session_key_tdb: restored schannel info key %s\n",
+ keystr));
+
+ status = NT_STATUS_OK;
+
+ done:
+
+ talloc_free(keystr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(creds);
+ return status;
+ }
+
+ *pcreds = creds;
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************************
+ Wrapper around schannel_fetch_session_key_tdb()
+ Note we must be root here.
+*******************************************************************************/
+
+NTSTATUS schannel_get_creds_state(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *computer_name,
+ struct netlogon_creds_CredentialState **_creds)
+{
+ TALLOC_CTX *tmpctx;
+ struct db_context *db_sc;
+ struct netlogon_creds_CredentialState *creds;
+ NTSTATUS status;
+
+ tmpctx = talloc_named(mem_ctx, 0, "schannel_get_creds_state");
+ if (!tmpctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ db_sc = open_schannel_session_store(tmpctx, lp_ctx);
+ if (!db_sc) {
+ TALLOC_FREE(tmpctx);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = schannel_fetch_session_key_tdb(db_sc, tmpctx,
+ computer_name, &creds);
+ if (NT_STATUS_IS_OK(status)) {
+ *_creds = talloc_steal(mem_ctx, creds);
+ if (!*_creds) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ talloc_free(tmpctx);
+ return status;
+}
+
+/******************************************************************************
+ Wrapper around schannel_store_session_key_tdb()
+ Note we must be root here.
+*******************************************************************************/
+
+NTSTATUS schannel_save_creds_state(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct netlogon_creds_CredentialState *creds)
+{
+ TALLOC_CTX *tmpctx;
+ struct db_context *db_sc;
+ NTSTATUS status;
+
+ tmpctx = talloc_named(mem_ctx, 0, "schannel_save_creds_state");
+ if (!tmpctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ db_sc = open_schannel_session_store(tmpctx, lp_ctx);
+ if (!db_sc) {
+ status = NT_STATUS_ACCESS_DENIED;
+ goto fail;
+ }
+
+ status = schannel_store_session_key_tdb(db_sc, tmpctx, creds);
+
+fail:
+ talloc_free(tmpctx);
+ return status;
+}
+
+
+/*
+ * Create a very lossy hash of the computer name.
+ *
+ * The idea here is to compress the computer name into small space so
+ * that malicious clients cannot fill the database with junk, as only a
+ * maximum of 16k of entries are possible.
+ *
+ * Collisions are certainly possible, and the design behaves in the
+ * same way as when the hostname is reused, but clients that use the
+ * same connection do not go via the cache, and the cache only needs
+ * to function between the ReqChallenge and ServerAuthenticate
+ * packets.
+ */
+static void hash_computer_name(const char *computer_name,
+ char keystr[16])
+{
+ unsigned int hash;
+ TDB_DATA computer_tdb_data = {
+ .dptr = (uint8_t *)discard_const_p(char, computer_name),
+ .dsize = strlen(computer_name)
+ };
+ hash = tdb_jenkins_hash(&computer_tdb_data);
+
+ /* we are using 14 bits of the digest to index our connections, so
+ that we use at most 16,384 buckets.*/
+ snprintf(keystr, 15, "CHALLENGE/%x%x", hash & 0xFF,
+ (hash & 0xFF00 >> 8) & 0x3f);
+ return;
+}
+
+
+static
+NTSTATUS schannel_store_challenge_tdb(struct db_context *db_sc,
+ TALLOC_CTX *mem_ctx,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const char *computer_name)
+{
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ TDB_DATA value;
+ char *name_upper = NULL;
+ NTSTATUS status;
+ char keystr[16] = { 0, };
+ struct netlogon_cache_entry cache_entry;
+
+ if (strlen(computer_name) > 255) {
+ /*
+ * We don't make this a limit at 15 chars as Samba has
+ * a test showing this can be longer :-(
+ */
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ name_upper = strupper_talloc(mem_ctx, computer_name);
+ if (name_upper == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ hash_computer_name(name_upper, keystr);
+
+ cache_entry.computer_name = name_upper;
+ cache_entry.client_challenge = *client_challenge;
+ cache_entry.server_challenge = *server_challenge;
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, &cache_entry,
+ (ndr_push_flags_fn_t)ndr_push_netlogon_cache_entry);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ value.dptr = blob.data;
+ value.dsize = blob.length;
+
+ status = dbwrap_store_bystring(db_sc, keystr, value, TDB_REPLACE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s: failed to stored challenge info for '%s' "
+ "with key %s - %s\n",
+ __func__, cache_entry.computer_name, keystr,
+ nt_errstr(status)));
+ return status;
+ }
+
+ DEBUG(3,("%s: stored challenge info for '%s' with key %s\n",
+ __func__, cache_entry.computer_name, keystr));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(netlogon_cache_entry, &cache_entry);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ Fetch a single challenge from the TDB.
+ ********************************************************************/
+
+static
+NTSTATUS schannel_fetch_challenge_tdb(struct db_context *db_sc,
+ TALLOC_CTX *mem_ctx,
+ struct netr_Credential *client_challenge,
+ struct netr_Credential *server_challenge,
+ const char *computer_name)
+{
+ NTSTATUS status;
+ TDB_DATA value;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ char keystr[16] = { 0, };
+ struct netlogon_cache_entry cache_entry;
+ char *name_upper = NULL;
+
+ name_upper = strupper_talloc(mem_ctx, computer_name);
+ if (name_upper == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ hash_computer_name(name_upper, keystr);
+
+ status = dbwrap_fetch_bystring(db_sc, mem_ctx, keystr, &value);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3,("%s: Failed to find entry for %s with key %s - %s\n",
+ __func__, name_upper, keystr, nt_errstr(status)));
+ goto done;
+ }
+
+ blob = data_blob_const(value.dptr, value.dsize);
+
+ ndr_err = ndr_pull_struct_blob_all(&blob, mem_ctx, &cache_entry,
+ (ndr_pull_flags_fn_t)ndr_pull_netlogon_cache_entry);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(3,("%s: Failed to parse entry for %s with key %s - %s\n",
+ __func__, name_upper, keystr, nt_errstr(status)));
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(netlogon_cache_entry, &cache_entry);
+ }
+
+ if (strcmp(cache_entry.computer_name, name_upper) != 0) {
+ status = NT_STATUS_NOT_FOUND;
+
+ DEBUG(1, ("%s: HASH COLLISION with key %s ! "
+ "Wanted to fetch record for %s but got %s.",
+ __func__, keystr, name_upper,
+ cache_entry.computer_name));
+ } else {
+
+ DEBUG(3,("%s: restored key %s for %s\n",
+ __func__, keystr, cache_entry.computer_name));
+
+ *client_challenge = cache_entry.client_challenge;
+ *server_challenge = cache_entry.server_challenge;
+ }
+ done:
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************************
+ Wrapper around schannel_fetch_challenge_tdb()
+ Note we must be root here.
+
+*******************************************************************************/
+
+NTSTATUS schannel_get_challenge(struct loadparm_context *lp_ctx,
+ struct netr_Credential *client_challenge,
+ struct netr_Credential *server_challenge,
+ const char *computer_name)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct db_context *db_sc;
+ NTSTATUS status;
+
+ db_sc = open_schannel_session_store(frame, lp_ctx);
+ if (!db_sc) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = schannel_fetch_challenge_tdb(db_sc, frame,
+ client_challenge,
+ server_challenge,
+ computer_name);
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/******************************************************************************
+ Wrapper around dbwrap_delete_bystring()
+ Note we must be root here.
+
+ This allows the challenge to be removed from the TDB, which should be
+ as soon as the TDB or in-memory copy it is used, to avoid reuse.
+*******************************************************************************/
+
+NTSTATUS schannel_delete_challenge(struct loadparm_context *lp_ctx,
+ const char *computer_name)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct db_context *db_sc;
+ char *name_upper;
+ char keystr[16] = { 0, };
+
+ db_sc = open_schannel_session_store(frame, lp_ctx);
+ if (!db_sc) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ name_upper = strupper_talloc(frame, computer_name);
+ if (!name_upper) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ hash_computer_name(name_upper, keystr);
+
+ /* Now delete it, we do not want to permit fetch of this twice */
+ dbwrap_delete_bystring(db_sc, keystr);
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
+/******************************************************************************
+ Wrapper around schannel_store_session_key_tdb()
+ Note we must be root here.
+*******************************************************************************/
+
+NTSTATUS schannel_save_challenge(struct loadparm_context *lp_ctx,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const char *computer_name)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct db_context *db_sc;
+ NTSTATUS status;
+
+ db_sc = open_schannel_session_store(frame, lp_ctx);
+ if (!db_sc) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = schannel_store_challenge_tdb(db_sc, frame,
+ client_challenge,
+ server_challenge,
+ computer_name);
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/********************************************************************
+ Validate an incoming authenticator against the credentials for the
+ remote machine stored in the schannel database.
+
+ The credentials are (re)read and from the schannel database, and
+ written back after the caclulations are performed.
+
+ If the creds_out parameter is not NULL returns the credentials.
+ ********************************************************************/
+
+NTSTATUS schannel_check_creds_state(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *computer_name,
+ struct netr_Authenticator *received_authenticator,
+ struct netr_Authenticator *return_authenticator,
+ struct netlogon_creds_CredentialState **creds_out)
+{
+ TALLOC_CTX *tmpctx;
+ struct db_context *db_sc;
+ struct netlogon_creds_CredentialState *creds;
+ NTSTATUS status;
+ char *name_upper = NULL;
+ char *keystr = NULL;
+ struct db_record *record;
+ TDB_DATA key;
+
+ if (creds_out != NULL) {
+ *creds_out = NULL;
+ }
+
+ tmpctx = talloc_named(mem_ctx, 0, "schannel_check_creds_state");
+ if (!tmpctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ name_upper = strupper_talloc(tmpctx, computer_name);
+ if (!name_upper) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ keystr = talloc_asprintf(tmpctx, "%s/%s",
+ SECRETS_SCHANNEL_STATE, name_upper);
+ if (!keystr) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ key = string_term_tdb_data(keystr);
+
+ db_sc = open_schannel_session_store(tmpctx, lp_ctx);
+ if (!db_sc) {
+ status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ record = dbwrap_fetch_locked(db_sc, tmpctx, key);
+ if (!record) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto done;
+ }
+
+ /* Because this is a shared structure (even across
+ * disconnects) we must update the database every time we
+ * update the structure */
+
+ status = schannel_fetch_session_key_tdb(db_sc, tmpctx,
+ computer_name, &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = netlogon_creds_server_step_check(creds,
+ received_authenticator,
+ return_authenticator);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = schannel_store_session_key_tdb(db_sc, tmpctx, creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (creds_out) {
+ *creds_out = talloc_steal(mem_ctx, creds);
+ if (!*creds_out) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+
+ status = NT_STATUS_OK;
+
+done:
+ talloc_free(tmpctx);
+ return status;
+}
+
diff --git a/libcli/auth/session.c b/libcli/auth/session.c
new file mode 100644
index 0000000..43ce9d5
--- /dev/null
+++ b/libcli/auth/session.c
@@ -0,0 +1,243 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ code to encrypt/decrypt data using the user session key
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/auth/libcli_auth.h"
+
+/*
+ encrypt or decrypt a blob of data using the user session key
+ as used in lsa_SetSecret
+
+ before calling, the out blob must be initialised to be the same size
+ as the in blob
+*/
+int sess_crypt_blob(DATA_BLOB *out, const DATA_BLOB *in, const DATA_BLOB *session_key,
+ enum samba_gnutls_direction encrypt)
+{
+ int i, k, rc;
+
+ if (in->length % 8 != 0) {
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ for (i=0,k=0;
+ i<in->length;
+ i += 8, k += 7) {
+ uint8_t bin[8], bout[8], key[7];
+
+ memcpy(bin, &in->data[i], 8);
+
+ if (k + 7 > session_key->length) {
+ k = (session_key->length - k);
+ }
+ memcpy(key, &session_key->data[k], 7);
+
+ rc = des_crypt56_gnutls(bout, bin, key, encrypt);
+ if (rc != 0) {
+ return rc;
+ }
+
+ memcpy(&out->data[i], bout, 8);
+ }
+ return 0;
+}
+
+
+/*
+ a convenient wrapper around sess_crypt_blob() for strings, using the LSA convention
+
+ note that we round the length to a multiple of 8. This seems to be needed for
+ compatibility with windows
+
+ caller should free using data_blob_free()
+*/
+DATA_BLOB sess_encrypt_string(const char *str, const DATA_BLOB *session_key)
+{
+ DATA_BLOB ret, src;
+ int slen = strlen(str);
+ int dlen = (slen+7) & ~7;
+ int rc;
+
+ src = data_blob(NULL, 8+dlen);
+ if (!src.data) {
+ return data_blob(NULL, 0);
+ }
+
+ ret = data_blob(NULL, 8+dlen);
+ if (!ret.data) {
+ data_blob_free(&src);
+ return data_blob(NULL, 0);
+ }
+
+ SIVAL(src.data, 0, slen);
+ SIVAL(src.data, 4, 1);
+ memset(src.data+8, 0, dlen);
+ memcpy(src.data+8, str, slen);
+
+ rc = sess_crypt_blob(&ret, &src, session_key, SAMBA_GNUTLS_ENCRYPT);
+
+ data_blob_free(&src);
+ if (rc != 0) {
+ data_blob_free(&ret);
+ return data_blob(NULL, 0);
+ }
+
+ return ret;
+}
+
+/*
+ a convenient wrapper around sess_crypt_blob() for strings, using the LSA convention
+
+ caller should free the returned string
+*/
+char *sess_decrypt_string(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob, const DATA_BLOB *session_key)
+{
+ DATA_BLOB out;
+ int rc, slen;
+ char *ret;
+
+ if (blob->length < 8) {
+ return NULL;
+ }
+
+ out = data_blob_talloc(mem_ctx, NULL, blob->length);
+ if (!out.data) {
+ return NULL;
+ }
+
+ rc = sess_crypt_blob(&out, blob, session_key, SAMBA_GNUTLS_DECRYPT);
+ if (rc != 0) {
+ data_blob_free(&out);
+ return NULL;
+ }
+
+ if (IVAL(out.data, 4) != 1) {
+ DEBUG(0,("Unexpected revision number %d in session crypted string\n",
+ IVAL(out.data, 4)));
+ data_blob_free(&out);
+ return NULL;
+ }
+
+ slen = IVAL(out.data, 0);
+ if (slen > blob->length - 8) {
+ DEBUG(0,("Invalid crypt length %d\n", slen));
+ data_blob_free(&out);
+ return NULL;
+ }
+
+ ret = talloc_strndup(mem_ctx, (const char *)(out.data+8), slen);
+
+ data_blob_free(&out);
+
+ DEBUG(0,("decrypted string '%s' of length %d\n", ret, slen));
+
+ return ret;
+}
+
+/*
+ a convenient wrapper around sess_crypt_blob() for DATA_BLOBs, using the LSA convention
+
+ note that we round the length to a multiple of 8. This seems to be needed for
+ compatibility with windows
+
+ caller should free using data_blob_free()
+*/
+DATA_BLOB sess_encrypt_blob(TALLOC_CTX *mem_ctx, DATA_BLOB *blob_in, const DATA_BLOB *session_key)
+{
+ DATA_BLOB ret, src;
+ int dlen = (blob_in->length+7) & ~7;
+ int rc;
+
+ src = data_blob_talloc(mem_ctx, NULL, 8+dlen);
+ if (!src.data) {
+ return data_blob(NULL, 0);
+ }
+
+ ret = data_blob_talloc(mem_ctx, NULL, 8+dlen);
+ if (!ret.data) {
+ data_blob_free(&src);
+ return data_blob(NULL, 0);
+ }
+
+ SIVAL(src.data, 0, blob_in->length);
+ SIVAL(src.data, 4, 1);
+ memset(src.data+8, 0, dlen);
+ memcpy(src.data+8, blob_in->data, blob_in->length);
+
+ rc = sess_crypt_blob(&ret, &src, session_key, SAMBA_GNUTLS_ENCRYPT);
+
+ data_blob_free(&src);
+ if (rc != 0) {
+ data_blob_free(&ret);
+ return data_blob(NULL, 0);
+ }
+
+ return ret;
+}
+
+/*
+ Decrypt a DATA_BLOB using the LSA convention
+*/
+NTSTATUS sess_decrypt_blob(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, const DATA_BLOB *session_key,
+ DATA_BLOB *ret)
+{
+ DATA_BLOB out;
+ int rc, slen;
+
+ if (blob->length < 8) {
+ DEBUG(0, ("Unexpected length %d in session crypted secret (BLOB)\n",
+ (int)blob->length));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ out = data_blob_talloc(mem_ctx, NULL, blob->length);
+ if (!out.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = sess_crypt_blob(&out, blob, session_key, SAMBA_GNUTLS_DECRYPT);
+ if (rc != 0) {
+ data_blob_free(&out);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ if (IVAL(out.data, 4) != 1) {
+ DEBUG(2,("Unexpected revision number %d in session crypted secret (BLOB)\n",
+ IVAL(out.data, 4)));
+ return NT_STATUS_UNKNOWN_REVISION;
+ }
+
+ slen = IVAL(out.data, 0);
+ if (slen > blob->length - 8) {
+ DEBUG(0,("Invalid crypt length %d in session crypted secret (BLOB)\n", slen));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ *ret = data_blob_talloc(mem_ctx, out.data+8, slen);
+ if (slen && !ret->data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ data_blob_free(&out);
+
+ return NT_STATUS_OK;
+}
diff --git a/libcli/auth/smbdes.c b/libcli/auth/smbdes.c
new file mode 100644
index 0000000..c6c4441
--- /dev/null
+++ b/libcli/auth/smbdes.c
@@ -0,0 +1,213 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a partial implementation of DES designed for use in the
+ SMB authentication protocol
+
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/auth/libcli_auth.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+static void str_to_key(const uint8_t *str,uint8_t *key)
+{
+ int i;
+
+ key[0] = str[0]>>1;
+ key[1] = ((str[0]&0x01)<<6) | (str[1]>>2);
+ key[2] = ((str[1]&0x03)<<5) | (str[2]>>3);
+ key[3] = ((str[2]&0x07)<<4) | (str[3]>>4);
+ key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5);
+ key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6);
+ key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7);
+ key[7] = str[6]&0x7F;
+ for (i=0;i<8;i++) {
+ key[i] = (key[i]<<1);
+ }
+}
+
+int des_crypt56_gnutls(uint8_t out[8], const uint8_t in[8],
+ const uint8_t key_in[7],
+ enum samba_gnutls_direction encrypt)
+{
+ /*
+ * A single block DES-CBC op, with an all-zero IV is the same as DES
+ * because the IV is combined with the data using XOR.
+ * This allows us to use GNUTLS_CIPHER_DES_CBC from GnuTLS and not
+ * implement single-DES in Samba.
+ *
+ * In turn this is used to build DES-ECB, which is used
+ * for example in the NTLM challenge/response calculation.
+ */
+ static const uint8_t iv8[8];
+ gnutls_datum_t iv = { discard_const(iv8), 8 };
+ gnutls_datum_t key;
+ gnutls_cipher_hd_t ctx;
+ uint8_t key2[8];
+ uint8_t outb[8];
+ int ret;
+
+ memset(out, 0, 8);
+
+ str_to_key(key_in, key2);
+
+ key.data = key2;
+ key.size = 8;
+
+ ret = gnutls_global_init();
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = gnutls_cipher_init(&ctx, GNUTLS_CIPHER_DES_CBC, &key, &iv);
+ if (ret != 0) {
+ return ret;
+ }
+
+ memcpy(outb, in, 8);
+ if (encrypt == SAMBA_GNUTLS_ENCRYPT) {
+ ret = gnutls_cipher_encrypt(ctx, outb, 8);
+ } else {
+ ret = gnutls_cipher_decrypt(ctx, outb, 8);
+ }
+
+ if (ret == 0) {
+ memcpy(out, outb, 8);
+ }
+
+ gnutls_cipher_deinit(ctx);
+
+ return ret;
+}
+
+int E_P16(const uint8_t *p14,uint8_t *p16)
+{
+ const uint8_t sp8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
+ int ret;
+
+ ret = des_crypt56_gnutls(p16, sp8, p14, SAMBA_GNUTLS_ENCRYPT);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return des_crypt56_gnutls(p16+8, sp8, p14+7, SAMBA_GNUTLS_ENCRYPT);
+}
+
+int E_P24(const uint8_t *p21, const uint8_t *c8, uint8_t *p24)
+{
+ int ret;
+
+ ret = des_crypt56_gnutls(p24, c8, p21, SAMBA_GNUTLS_ENCRYPT);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = des_crypt56_gnutls(p24+8, c8, p21+7, SAMBA_GNUTLS_ENCRYPT);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return des_crypt56_gnutls(p24+16, c8, p21+14, SAMBA_GNUTLS_ENCRYPT);
+}
+
+int E_old_pw_hash( uint8_t *p14, const uint8_t *in, uint8_t *out)
+{
+ int ret;
+
+ ret = des_crypt56_gnutls(out, in, p14, SAMBA_GNUTLS_ENCRYPT);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return des_crypt56_gnutls(out+8, in+8, p14+7, SAMBA_GNUTLS_ENCRYPT);
+}
+
+/* des encryption with a 128 bit key */
+int des_crypt128(uint8_t out[8], const uint8_t in[8], const uint8_t key[16])
+{
+ uint8_t buf[8];
+ int ret;
+
+ ret = des_crypt56_gnutls(buf, in, key, SAMBA_GNUTLS_ENCRYPT);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return des_crypt56_gnutls(out, buf, key+9, SAMBA_GNUTLS_ENCRYPT);
+}
+
+/* des encryption with a 112 bit (14 byte) key */
+int des_crypt112(uint8_t out[8], const uint8_t in[8], const uint8_t key[14],
+ enum samba_gnutls_direction encrypt)
+{
+ uint8_t buf[8];
+ int ret;
+
+ if (encrypt == SAMBA_GNUTLS_ENCRYPT) {
+ ret = des_crypt56_gnutls(buf, in, key, SAMBA_GNUTLS_ENCRYPT);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return des_crypt56_gnutls(out, buf, key+7, SAMBA_GNUTLS_ENCRYPT);
+ }
+
+ ret = des_crypt56_gnutls(buf, in, key+7, SAMBA_GNUTLS_DECRYPT);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return des_crypt56_gnutls(out, buf, key, SAMBA_GNUTLS_DECRYPT);
+}
+
+/* des encryption of a 16 byte lump of data with a 112 bit key */
+int des_crypt112_16(uint8_t out[16], const uint8_t in[16], const uint8_t key[14],
+ enum samba_gnutls_direction encrypt)
+{
+ int ret;
+
+ ret = des_crypt56_gnutls(out, in, key, encrypt);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return des_crypt56_gnutls(out + 8, in + 8, key+7, encrypt);
+}
+
+/* Decode a sam password hash into a password. The password hash is the
+ same method used to store passwords in the NT registry. The DES key
+ used is based on the RID of the user. */
+int sam_rid_crypt(unsigned int rid, const uint8_t *in, uint8_t *out,
+ enum samba_gnutls_direction encrypt)
+{
+ uint8_t s[14];
+ int ret;
+
+ s[0] = s[4] = s[8] = s[12] = (uint8_t)(rid & 0xFF);
+ s[1] = s[5] = s[9] = s[13] = (uint8_t)((rid >> 8) & 0xFF);
+ s[2] = s[6] = s[10] = (uint8_t)((rid >> 16) & 0xFF);
+ s[3] = s[7] = s[11] = (uint8_t)((rid >> 24) & 0xFF);
+
+ ret = des_crypt56_gnutls(out, in, s, encrypt);
+ if (ret != 0) {
+ return ret;
+ }
+ return des_crypt56_gnutls(out+8, in+8, s+7, encrypt);
+}
diff --git a/libcli/auth/smbencrypt.c b/libcli/auth/smbencrypt.c
new file mode 100644
index 0000000..8492202
--- /dev/null
+++ b/libcli/auth/smbencrypt.c
@@ -0,0 +1,1325 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1998
+ Modified by Jeremy Allison 1995.
+ Copyright (C) Jeremy Allison 1995-2000.
+ Copyright (C) Luke Kennethc Casson Leighton 1996-2000.
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include "../libcli/auth/msrpc_parse.h"
+#include "../lib/crypto/crypto.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../librpc/gen_ndr/ndr_ntlmssp.h"
+#include "lib/util/bytearray.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+int SMBencrypt_hash(const uint8_t lm_hash[16], const uint8_t *c8, uint8_t p24[24])
+{
+ uint8_t p21[21];
+ int rc;
+
+ memset(p21,'\0',21);
+ memcpy(p21, lm_hash, 16);
+
+ rc = SMBOWFencrypt(p21, c8, p24);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("SMBencrypt_hash: lm#, challenge, response\n"));
+ dump_data(100, p21, 16);
+ dump_data(100, c8, 8);
+ dump_data(100, p24, 24);
+#endif
+
+ return rc;
+}
+
+/*
+ This implements the X/Open SMB password encryption
+ It takes a password ('unix' string), a 8 byte "crypt key"
+ and puts 24 bytes of encrypted password into p24
+
+ Returns False if password must have been truncated to create LM hash
+*/
+
+bool SMBencrypt(const char *passwd, const uint8_t *c8, uint8_t p24[24])
+{
+ bool ret;
+ uint8_t lm_hash[16];
+ int rc;
+
+ ret = E_deshash(passwd, lm_hash);
+ rc = SMBencrypt_hash(lm_hash, c8, p24);
+ if (rc != 0) {
+ ret = false;
+ }
+ return ret;
+}
+
+/**
+ * Creates the MD4 Hash of the users password in NT UNICODE.
+ * @param passwd password in 'unix' charset.
+ * @param p16 return password hashed with md4, caller allocated 16 byte buffer
+ */
+
+bool E_md4hash(const char *passwd, uint8_t p16[16])
+{
+ size_t len;
+ smb_ucs2_t *wpwd;
+ bool ret;
+
+ ret = push_ucs2_talloc(NULL, &wpwd, passwd, &len);
+ if (!ret || len < 2) {
+ /* We don't want to return fixed data, as most callers
+ * don't check */
+ mdfour(p16, (const uint8_t *)passwd, strlen(passwd));
+ return false;
+ }
+
+ len -= 2;
+ mdfour(p16, (const uint8_t *)wpwd, len);
+
+ talloc_free(wpwd);
+ return true;
+}
+
+/**
+ * Creates the DES forward-only Hash of the users password in DOS ASCII charset
+ * @param passwd password in 'unix' charset.
+ * @param p16 return password hashed with DES, caller allocated 16 byte buffer
+ * @return false if password was > 14 characters, and therefore may be incorrect, otherwise true
+ * @note p16 is filled in regardless
+ */
+
+bool E_deshash(const char *passwd, uint8_t p16[16])
+{
+ bool ret;
+ int rc;
+ uint8_t dospwd[14];
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ size_t converted_size;
+
+ char *tmpbuf;
+
+ ZERO_STRUCT(dospwd);
+
+ tmpbuf = strupper_talloc(frame, passwd);
+ if (tmpbuf == NULL) {
+ /* Too many callers don't check this result, we need to fill in the buffer with something */
+ strlcpy((char *)dospwd, passwd ? passwd : "", sizeof(dospwd));
+ E_P16(dospwd, p16);
+ talloc_free(frame);
+ return false;
+ }
+
+ ZERO_STRUCT(dospwd);
+
+ ret = convert_string_error(CH_UNIX, CH_DOS, tmpbuf, strlen(tmpbuf), dospwd, sizeof(dospwd), &converted_size);
+ talloc_free(frame);
+
+ /* Only the first 14 chars are considered, password need not
+ * be null terminated. We do this in the error and success
+ * case to avoid returning a fixed 'password' buffer, but
+ * callers should not use it when E_deshash returns false */
+
+ rc = E_P16((const uint8_t *)dospwd, p16);
+ if (rc != 0) {
+ ret = false;
+ }
+
+ ZERO_STRUCT(dospwd);
+
+ return ret;
+}
+
+/**
+ * Creates the MD4 and DES (LM) Hash of the users password.
+ * MD4 is of the NT Unicode, DES is of the DOS UPPERCASE password.
+ * @param passwd password in 'unix' charset.
+ * @param nt_p16 return password hashed with md4, caller allocated 16 byte buffer
+ * @param p16 return password hashed with des, caller allocated 16 byte buffer
+ */
+
+/* Does both the NT and LM owfs of a user's password */
+void nt_lm_owf_gen(const char *pwd, uint8_t nt_p16[16], uint8_t p16[16])
+{
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ memset(nt_p16, '\0', 16);
+ E_md4hash(pwd, nt_p16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("nt_lm_owf_gen: pwd, nt#\n"));
+ dump_data(120, (const uint8_t *)pwd, strlen(pwd));
+ dump_data(100, nt_p16, 16);
+#endif
+
+ E_deshash(pwd, (uint8_t *)p16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("nt_lm_owf_gen: pwd, lm#\n"));
+ dump_data(120, (const uint8_t *)pwd, strlen(pwd));
+ dump_data(100, p16, 16);
+#endif
+}
+
+/* Does both the NTLMv2 owfs of a user's password */
+bool ntv2_owf_gen(const uint8_t owf[16],
+ const char *user_in, const char *domain_in,
+ uint8_t kr_buf[16])
+{
+ smb_ucs2_t *user;
+ smb_ucs2_t *domain;
+ size_t user_byte_len;
+ size_t domain_byte_len;
+ gnutls_hmac_hd_t hmac_hnd = NULL;
+ int rc;
+ bool ok = false;
+ TALLOC_CTX *mem_ctx = talloc_init("ntv2_owf_gen for %s\\%s", domain_in, user_in);
+
+ if (!mem_ctx) {
+ return false;
+ }
+
+ if (!user_in) {
+ user_in = "";
+ }
+
+ if (!domain_in) {
+ domain_in = "";
+ }
+
+ user_in = strupper_talloc(mem_ctx, user_in);
+ if (user_in == NULL) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ok = push_ucs2_talloc(mem_ctx, &user, user_in, &user_byte_len );
+ if (!ok) {
+ DEBUG(0, ("push_uss2_talloc() for user failed)\n"));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ok = push_ucs2_talloc(mem_ctx, &domain, domain_in, &domain_byte_len);
+ if (!ok) {
+ DEBUG(0, ("push_ucs2_talloc() for domain failed\n"));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ SMB_ASSERT(user_byte_len >= 2);
+ SMB_ASSERT(domain_byte_len >= 2);
+
+ /* We don't want null termination */
+ user_byte_len = user_byte_len - 2;
+ domain_byte_len = domain_byte_len - 2;
+
+ rc = gnutls_hmac_init(&hmac_hnd,
+ GNUTLS_MAC_MD5,
+ owf,
+ 16);
+ if (rc < 0) {
+ ok = false;
+ goto out;
+ }
+
+ rc = gnutls_hmac(hmac_hnd, user, user_byte_len);
+ if (rc < 0) {
+ gnutls_hmac_deinit(hmac_hnd, NULL);
+ ok = false;
+ goto out;
+ }
+ rc = gnutls_hmac(hmac_hnd, domain, domain_byte_len);
+ if (rc < 0) {
+ gnutls_hmac_deinit(hmac_hnd, NULL);
+ ok = false;
+ goto out;
+ }
+
+ gnutls_hmac_deinit(hmac_hnd, kr_buf);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("ntv2_owf_gen: user, domain, owfkey, kr\n"));
+ dump_data(100, (uint8_t *)user, user_byte_len);
+ dump_data(100, (uint8_t *)domain, domain_byte_len);
+ dump_data(100, owf, 16);
+ dump_data(100, kr_buf, 16);
+#endif
+
+ ok = true;
+out:
+ talloc_free(mem_ctx);
+ return ok;
+}
+
+/* Does the des encryption from the NT or LM MD4 hash. */
+int SMBOWFencrypt(const uint8_t passwd[16], const uint8_t *c8, uint8_t p24[24])
+{
+ uint8_t p21[21];
+
+ ZERO_STRUCT(p21);
+
+ memcpy(p21, passwd, 16);
+ return E_P24(p21, c8, p24);
+}
+
+/* Does the des encryption. */
+
+int SMBNTencrypt_hash(const uint8_t nt_hash[16], const uint8_t *c8, uint8_t *p24)
+{
+ uint8_t p21[21];
+ int rc;
+
+ memset(p21,'\0',21);
+ memcpy(p21, nt_hash, 16);
+ rc = SMBOWFencrypt(p21, c8, p24);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("SMBNTencrypt: nt#, challenge, response\n"));
+ dump_data(100, p21, 16);
+ dump_data(100, c8, 8);
+ dump_data(100, p24, 24);
+#endif
+
+ return rc;
+}
+
+/* Does the NT MD4 hash then des encryption. Plaintext version of the above. */
+
+int SMBNTencrypt(const char *passwd, const uint8_t *c8, uint8_t *p24)
+{
+ uint8_t nt_hash[16];
+ E_md4hash(passwd, nt_hash);
+ return SMBNTencrypt_hash(nt_hash, c8, p24);
+}
+
+
+/* Does the md5 encryption from the Key Response for NTLMv2. */
+NTSTATUS SMBOWFencrypt_ntv2(const uint8_t kr[16],
+ const DATA_BLOB *srv_chal,
+ const DATA_BLOB *smbcli_chal,
+ uint8_t resp_buf[16])
+{
+ gnutls_hmac_hd_t hmac_hnd = NULL;
+ NTSTATUS status;
+ int rc;
+
+ rc = gnutls_hmac_init(&hmac_hnd,
+ GNUTLS_MAC_MD5,
+ kr,
+ 16);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED);
+ }
+
+ rc = gnutls_hmac(hmac_hnd, srv_chal->data, srv_chal->length);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED);
+ goto out;
+ }
+ rc = gnutls_hmac(hmac_hnd, smbcli_chal->data, smbcli_chal->length);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED);
+ goto out;
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBOWFencrypt_ntv2: srv_chal, smbcli_chal, resp_buf\n"));
+ dump_data(100, srv_chal->data, srv_chal->length);
+ dump_data(100, smbcli_chal->data, smbcli_chal->length);
+ dump_data(100, resp_buf, 16);
+#endif
+
+ status = NT_STATUS_OK;
+out:
+ gnutls_hmac_deinit(hmac_hnd, resp_buf);
+ return status;
+}
+
+NTSTATUS SMBsesskeygen_ntv2(const uint8_t kr[16],
+ const uint8_t *nt_resp,
+ uint8_t sess_key[16])
+{
+ int rc;
+
+ /* a very nice, 128 bit, variable session key */
+ rc = gnutls_hmac_fast(GNUTLS_MAC_MD5,
+ kr,
+ 16,
+ nt_resp,
+ 16,
+ sess_key);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_ntv2:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+
+ return NT_STATUS_OK;
+}
+
+void SMBsesskeygen_ntv1(const uint8_t kr[16], uint8_t sess_key[16])
+{
+ /* yes, this session key does not change - yes, this
+ is a problem - but it is 128 bits */
+
+ mdfour((uint8_t *)sess_key, kr, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_ntv1:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+NTSTATUS SMBsesskeygen_lm_sess_key(const uint8_t lm_hash[16],
+ const uint8_t lm_resp[24], /* only uses 8 */
+ uint8_t sess_key[16])
+{
+ /* Calculate the LM session key (effective length 40 bits,
+ but changes with each session) */
+ uint8_t p24[24];
+ uint8_t partial_lm_hash[14];
+ int rc;
+
+ memcpy(partial_lm_hash, lm_hash, 8);
+ memset(partial_lm_hash + 8, 0xbd, 6);
+
+ rc = des_crypt56_gnutls(p24, lm_resp, partial_lm_hash, SAMBA_GNUTLS_ENCRYPT);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+ rc = des_crypt56_gnutls(p24+8, lm_resp, partial_lm_hash + 7, SAMBA_GNUTLS_ENCRYPT);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ memcpy(sess_key, p24, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_lm_sess_key: \n"));
+ dump_data(100, sess_key, 16);
+#endif
+
+ return NT_STATUS_OK;
+}
+
+DATA_BLOB NTLMv2_generate_names_blob(TALLOC_CTX *mem_ctx,
+ const char *hostname,
+ const char *domain)
+{
+ DATA_BLOB names_blob = data_blob_talloc(mem_ctx, NULL, 0);
+
+ /* Deliberately ignore return here.. */
+ if (hostname != NULL) {
+ (void)msrpc_gen(mem_ctx, &names_blob,
+ "aaa",
+ MsvAvNbDomainName, domain,
+ MsvAvNbComputerName, hostname,
+ MsvAvEOL, "");
+ } else {
+ (void)msrpc_gen(mem_ctx, &names_blob,
+ "aa",
+ MsvAvNbDomainName, domain,
+ MsvAvEOL, "");
+ }
+ return names_blob;
+}
+
+static DATA_BLOB NTLMv2_generate_client_data(TALLOC_CTX *mem_ctx,
+ NTTIME nttime,
+ const DATA_BLOB *names_blob)
+{
+ uint8_t client_chal[8];
+ DATA_BLOB response = data_blob(NULL, 0);
+ uint8_t long_date[8];
+
+ generate_random_buffer(client_chal, sizeof(client_chal));
+
+ push_nttime(long_date, 0, nttime);
+
+ /* See http://www.ubiqx.org/cifs/SMB.html#SMB.8.5 */
+
+ /* Deliberately ignore return here.. */
+ (void)msrpc_gen(mem_ctx, &response, "ddbbdb",
+ 0x00000101, /* Header */
+ 0, /* 'Reserved' */
+ long_date, 8, /* Timestamp */
+ client_chal, 8, /* client challenge */
+ 0, /* Unknown */
+ names_blob->data, names_blob->length); /* End of name list */
+
+ return response;
+}
+
+static DATA_BLOB NTLMv2_generate_response(TALLOC_CTX *out_mem_ctx,
+ const uint8_t ntlm_v2_hash[16],
+ const DATA_BLOB *server_chal,
+ NTTIME nttime,
+ const DATA_BLOB *names_blob)
+{
+ uint8_t ntlmv2_response[16];
+ DATA_BLOB ntlmv2_client_data;
+ DATA_BLOB final_response;
+ NTSTATUS status;
+
+ TALLOC_CTX *mem_ctx = talloc_named(out_mem_ctx, 0,
+ "NTLMv2_generate_response internal context");
+
+ if (!mem_ctx) {
+ return data_blob(NULL, 0);
+ }
+
+ /* NTLMv2 */
+ /* generate some data to pass into the response function - including
+ the hostname and domain name of the server */
+ ntlmv2_client_data = NTLMv2_generate_client_data(mem_ctx, nttime, names_blob);
+
+ /* Given that data, and the challenge from the server, generate a response */
+ status = SMBOWFencrypt_ntv2(ntlm_v2_hash,
+ server_chal,
+ &ntlmv2_client_data,
+ ntlmv2_response);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return data_blob(NULL, 0);
+ }
+
+ final_response = data_blob_talloc(out_mem_ctx, NULL, sizeof(ntlmv2_response) + ntlmv2_client_data.length);
+
+ memcpy(final_response.data, ntlmv2_response, sizeof(ntlmv2_response));
+
+ memcpy(final_response.data+sizeof(ntlmv2_response),
+ ntlmv2_client_data.data, ntlmv2_client_data.length);
+
+ talloc_free(mem_ctx);
+
+ return final_response;
+}
+
+static DATA_BLOB LMv2_generate_response(TALLOC_CTX *mem_ctx,
+ const uint8_t ntlm_v2_hash[16],
+ const DATA_BLOB *server_chal)
+{
+ uint8_t lmv2_response[16];
+ DATA_BLOB lmv2_client_data = data_blob_talloc(mem_ctx, NULL, 8);
+ DATA_BLOB final_response = data_blob_talloc(mem_ctx, NULL,24);
+ NTSTATUS status;
+
+ /* LMv2 */
+ /* client-supplied random data */
+ generate_random_buffer(lmv2_client_data.data, lmv2_client_data.length);
+
+ /* Given that data, and the challenge from the server, generate a response */
+ status = SMBOWFencrypt_ntv2(ntlm_v2_hash,
+ server_chal,
+ &lmv2_client_data,
+ lmv2_response);
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(&lmv2_client_data);
+ return data_blob(NULL, 0);
+ }
+ memcpy(final_response.data, lmv2_response, sizeof(lmv2_response));
+
+ /* after the first 16 bytes is the random data we generated above,
+ so the server can verify us with it */
+ memcpy(final_response.data+sizeof(lmv2_response),
+ lmv2_client_data.data, lmv2_client_data.length);
+
+ data_blob_free(&lmv2_client_data);
+
+ return final_response;
+}
+
+bool SMBNTLMv2encrypt_hash(TALLOC_CTX *mem_ctx,
+ const char *user, const char *domain, const uint8_t nt_hash[16],
+ const DATA_BLOB *server_chal,
+ const NTTIME *server_timestamp,
+ const DATA_BLOB *names_blob,
+ DATA_BLOB *lm_response, DATA_BLOB *nt_response,
+ DATA_BLOB *lm_session_key, DATA_BLOB *user_session_key)
+{
+ uint8_t ntlm_v2_hash[16];
+ NTSTATUS status;
+
+ /* We don't use the NT# directly. Instead we use it mashed up with
+ the username and domain.
+ This prevents username swapping during the auth exchange
+ */
+ if (!ntv2_owf_gen(nt_hash, user, domain, ntlm_v2_hash)) {
+ return false;
+ }
+
+ if (nt_response) {
+ const NTTIME *nttime = server_timestamp;
+ NTTIME _now = 0;
+
+ if (nttime == NULL) {
+ struct timeval tv_now = timeval_current();
+ _now = timeval_to_nttime(&tv_now);
+ nttime = &_now;
+ }
+
+ *nt_response = NTLMv2_generate_response(mem_ctx,
+ ntlm_v2_hash,
+ server_chal,
+ *nttime,
+ names_blob);
+ if (user_session_key) {
+ *user_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+
+ /* The NTLMv2 calculations also provide a session key, for signing etc later */
+ /* use only the first 16 bytes of nt_response for session key */
+ status = SMBsesskeygen_ntv2(ntlm_v2_hash,
+ nt_response->data,
+ user_session_key->data);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ }
+ }
+
+ /* LMv2 */
+
+ if (lm_response) {
+ if (server_timestamp != NULL) {
+ *lm_response = data_blob_talloc_zero(mem_ctx, 24);
+ } else {
+ *lm_response = LMv2_generate_response(mem_ctx,
+ ntlm_v2_hash,
+ server_chal);
+ }
+ if (lm_session_key) {
+ *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+
+ /* The NTLMv2 calculations also provide a session key, for signing etc later */
+ /* use only the first 16 bytes of lm_response for session key */
+ status = SMBsesskeygen_ntv2(ntlm_v2_hash,
+ lm_response->data,
+ lm_session_key->data);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool SMBNTLMv2encrypt(TALLOC_CTX *mem_ctx,
+ const char *user, const char *domain,
+ const char *password,
+ const DATA_BLOB *server_chal,
+ const DATA_BLOB *names_blob,
+ DATA_BLOB *lm_response, DATA_BLOB *nt_response,
+ DATA_BLOB *lm_session_key, DATA_BLOB *user_session_key)
+{
+ uint8_t nt_hash[16];
+ E_md4hash(password, nt_hash);
+
+ return SMBNTLMv2encrypt_hash(mem_ctx,
+ user, domain, nt_hash,
+ server_chal, NULL, names_blob,
+ lm_response, nt_response, lm_session_key, user_session_key);
+}
+
+NTSTATUS NTLMv2_RESPONSE_verify_netlogon_creds(const char *account_name,
+ const char *account_domain,
+ const DATA_BLOB response,
+ const struct netlogon_creds_CredentialState *creds,
+ const char *workgroup)
+{
+ TALLOC_CTX *frame = NULL;
+ /* RespType + HiRespType */
+ static const char *magic = "\x01\x01";
+ int cmp;
+ struct NTLMv2_RESPONSE v2_resp;
+ enum ndr_err_code err;
+ const struct AV_PAIR *av_nb_cn = NULL;
+ const struct AV_PAIR *av_nb_dn = NULL;
+
+ if (response.length < 48) {
+ /*
+ * NTLMv2_RESPONSE has at least 48 bytes.
+ */
+ return NT_STATUS_OK;
+ }
+
+ cmp = memcmp(response.data + 16, magic, 2);
+ if (cmp != 0) {
+ /*
+ * It doesn't look like a valid NTLMv2_RESPONSE
+ */
+ return NT_STATUS_OK;
+ }
+
+ if (response.length == 95) {
+ /*
+ * ndr_pull_NTLMv2_RESPONSE() fails on this strange blob,
+ * because the AvPairs content is not valid
+ * as AvLen of the first pair is 33032 (0x8108).
+ *
+ * I saw a single machine sending the following 3 times
+ * in a row, but I'm not sure if everything is static.
+ *
+ * Note this is NTLMv2_CLIENT_CHALLENGE only, not
+ * the full NTLMv2_RESPONSE (which has Response of 16 bytes
+ * before the NTLMv2_CLIENT_CHALLENGE).
+ *
+ * Note this code only prevents
+ * ndr_pull_error(Buffer Size Error): Pull bytes 39016
+ * debug message for a known case, the actual
+ * bug is also handled below in a generic way to
+ * map NT_STATUS_BUFFER_TOO_SMALL to NT_STATUS_OK.
+ *
+ * See https://bugzilla.samba.org/show_bug.cgi?id=14932
+ */
+ static const char *netapp_magic =
+ "\x01\x01\x00\x00\x00\x00\x00\x00"
+ "\x3f\x3f\x3f\x3f\x3f\x3f\x3f\x3f"
+ "\xb8\x82\x3a\xf1\xb3\xdd\x08\x15"
+ "\x00\x00\x00\x00\x11\xa2\x08\x81"
+ "\x50\x38\x22\x78\x2b\x94\x47\xfe"
+ "\x54\x94\x7b\xff\x17\x27\x5a\xb4"
+ "\xf4\x18\xba\xdc\x2c\x38\xfd\x5b"
+ "\xfb\x0e\xc1\x85\x1e\xcc\x92\xbb"
+ "\x9b\xb1\xc4\xd5\x53\x14\xff\x8c"
+ "\x76\x49\xf5\x45\x90\x19\xa2";
+ /*
+ * First we check the initial bytes
+ * and the 0x3F timestamp.
+ */
+ cmp = memcmp(response.data + 16,
+ netapp_magic,
+ 16);
+ if (cmp == 0) {
+ /*
+ * Then check everything after the
+ * client challenge
+ */
+ cmp = memcmp(response.data + 40,
+ netapp_magic + 24,
+ response.length - 40);
+ if (cmp == 0) {
+ DBG_DEBUG("Invalid NETAPP NTLMv2_RESPONSE "
+ "for user[%s\\%s] against "
+ "SEC_CHAN(%u)[%s/%s] "
+ "in workgroup[%s]\n",
+ account_domain,
+ account_name,
+ creds->secure_channel_type,
+ creds->computer_name,
+ creds->account_name,
+ workgroup);
+ return NT_STATUS_OK;
+ }
+ }
+ }
+
+ frame = talloc_stackframe();
+
+ err = ndr_pull_struct_blob(&response, frame, &v2_resp,
+ (ndr_pull_flags_fn_t)ndr_pull_NTLMv2_RESPONSE);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ NTSTATUS status;
+ status = ndr_map_error2ntstatus(err);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_BUFFER_TOO_SMALL)) {
+ /*
+ * We are supposed to ignore invalid buffers,
+ * see https://bugzilla.samba.org/show_bug.cgi?id=14932
+ */
+ status = NT_STATUS_OK;
+ }
+ DEBUG(2,("%s: Failed to parse NTLMv2_RESPONSE length=%u "
+ "for user[%s\\%s] against SEC_CHAN(%u)[%s/%s] "
+ "in workgroup[%s] - %s %s %s\n",
+ __func__,
+ (unsigned)response.length,
+ account_domain,
+ account_name,
+ creds->secure_channel_type,
+ creds->computer_name,
+ creds->account_name,
+ workgroup,
+ ndr_map_error2string(err),
+ NT_STATUS_IS_OK(status) ? "(ignoring) =>" : "=>",
+ nt_errstr(status)));
+ dump_data(2, response.data, response.length);
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ if (DEBUGLVL(10)) {
+ NDR_PRINT_DEBUG(NTLMv2_RESPONSE, &v2_resp);
+ }
+
+ /*
+ * Make sure the netbios computer name in the
+ * NTLMv2_RESPONSE matches the computer name
+ * in the secure channel credentials for workstation
+ * trusts.
+ *
+ * And the netbios domain name matches our
+ * workgroup.
+ *
+ * This prevents workstations from requesting
+ * the session key of NTLMSSP sessions of clients
+ * to other hosts.
+ */
+ if (creds->secure_channel_type == SEC_CHAN_WKSTA) {
+ av_nb_cn = ndr_ntlmssp_find_av(&v2_resp.Challenge.AvPairs,
+ MsvAvNbComputerName);
+ av_nb_dn = ndr_ntlmssp_find_av(&v2_resp.Challenge.AvPairs,
+ MsvAvNbDomainName);
+ }
+
+ if (av_nb_cn != NULL) {
+ const char *v = NULL;
+ char *a = NULL;
+ size_t len;
+
+ v = av_nb_cn->Value.AvNbComputerName;
+
+ a = talloc_strdup(frame, creds->account_name);
+ if (a == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ len = strlen(a);
+ if (len > 0 && a[len - 1] == '$') {
+ a[len - 1] = '\0';
+ }
+
+ cmp = strcasecmp_m(a, v);
+ if (cmp != 0) {
+ DEBUG(2,("%s: NTLMv2_RESPONSE with "
+ "NbComputerName[%s] rejected "
+ "for user[%s\\%s] "
+ "against SEC_CHAN_WKSTA[%s/%s] "
+ "in workgroup[%s]\n",
+ __func__, v,
+ account_domain,
+ account_name,
+ creds->computer_name,
+ creds->account_name,
+ workgroup));
+ TALLOC_FREE(frame);
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+ if (av_nb_dn != NULL) {
+ const char *v = NULL;
+
+ v = av_nb_dn->Value.AvNbDomainName;
+
+ cmp = strcasecmp_m(workgroup, v);
+ if (cmp != 0) {
+ DEBUG(2,("%s: NTLMv2_RESPONSE with "
+ "NbDomainName[%s] rejected "
+ "for user[%s\\%s] "
+ "against SEC_CHAN_WKSTA[%s/%s] "
+ "in workgroup[%s]\n",
+ __func__, v,
+ account_domain,
+ account_name,
+ creds->computer_name,
+ creds->account_name,
+ workgroup));
+ TALLOC_FREE(frame);
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
+enum encode_order {
+ ENCODE_ORDER_PASSWORD_FIRST,
+ ENCODE_ORDER_PASSWORD_LAST,
+};
+
+#define PASSWORD_BUFFER_LEN 512
+
+static ssize_t _encode_pwd_buffer_from_str(uint8_t buf[PASSWORD_BUFFER_LEN],
+ const char *password,
+ int string_flags,
+ enum encode_order order)
+{
+ ssize_t new_pw_len;
+ size_t pw_pos = 0;
+ size_t random_pos = 0;
+ size_t random_len = 0;
+
+ /* The incoming buffer can be any alignment. */
+ string_flags |= STR_NOALIGN;
+
+ new_pw_len = push_string(buf,
+ password,
+ PASSWORD_BUFFER_LEN,
+ string_flags);
+ if (new_pw_len < 0) {
+ BURN_DATA_SIZE(buf, PASSWORD_BUFFER_LEN);
+ return -1;
+ }
+
+ if (new_pw_len == PASSWORD_BUFFER_LEN) {
+ return new_pw_len;
+ }
+
+ switch (order) {
+ case ENCODE_ORDER_PASSWORD_FIRST:
+ pw_pos = 0;
+ random_pos = new_pw_len;
+ random_len = PASSWORD_BUFFER_LEN - random_pos;
+ break;
+ case ENCODE_ORDER_PASSWORD_LAST:
+ pw_pos = PASSWORD_BUFFER_LEN - new_pw_len;
+ random_pos = 0;
+ random_len = pw_pos;
+ memmove(buf + pw_pos, buf, new_pw_len);
+ break;
+ }
+
+ generate_random_buffer(buf + random_pos, random_len);
+
+ return new_pw_len;
+}
+
+/***********************************************************
+ encode a password buffer with a unicode password. The buffer
+ is filled with random data to make it harder to attack.
+************************************************************/
+bool encode_pw_buffer(uint8_t buffer[516], const char *password, int string_flags)
+{
+ ssize_t pw_len;
+
+ pw_len = _encode_pwd_buffer_from_str(buffer,
+ password,
+ string_flags,
+ ENCODE_ORDER_PASSWORD_LAST);
+ if (pw_len < 0 || pw_len > PASSWORD_BUFFER_LEN) {
+ return false;
+ }
+
+ PUSH_LE_U32(buffer, PASSWORD_BUFFER_LEN, pw_len);
+
+ return true;
+}
+
+
+/***********************************************************
+ decode a password buffer
+ *new_pw_len is the length in bytes of the possibly mulitbyte
+ returned password including termination.
+************************************************************/
+
+bool decode_pw_buffer(TALLOC_CTX *ctx,
+ uint8_t in_buffer[516],
+ char **pp_new_pwrd,
+ size_t *new_pw_len,
+ charset_t string_charset)
+{
+ DATA_BLOB new_password;
+ int byte_len=0;
+ bool ok;
+
+ *pp_new_pwrd = NULL;
+ *new_pw_len = 0;
+
+ ok = extract_pw_from_buffer(ctx, in_buffer, &new_password);
+ if (!ok) {
+ return false;
+ }
+
+ /*
+ Warning !!! : This function is called from some rpc call.
+ The password IN the buffer may be a UNICODE string.
+ The password IN new_pwrd is an ASCII string
+ If you reuse that code somewhere else check first.
+ */
+
+ /* decode into the return buffer. */
+ ok = convert_string_talloc(ctx,
+ string_charset,
+ CH_UNIX,
+ new_password.data,
+ new_password.length,
+ (void *)pp_new_pwrd,
+ new_pw_len);
+ data_blob_free(&new_password);
+ if (!ok) {
+ DBG_ERR("Failed to convert incoming password\n");
+ return false;
+ }
+ talloc_keep_secret(*pp_new_pwrd);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("decode_pw_buffer: new_pwrd: "));
+ dump_data(100, (uint8_t *)*pp_new_pwrd, *new_pw_len);
+ DEBUG(100,("multibyte len:%lu\n", (unsigned long int)*new_pw_len));
+ DEBUG(100,("original char len:%d\n", byte_len/2));
+#endif
+
+ return true;
+}
+
+#define MAX_PASSWORD_LEN 256
+
+/*
+ * [MS-SAMR] 2.2.6.32 This creates the buffer to be sent. It is of type
+ * SAMPR_USER_PASSWORD_AES.
+ */
+bool encode_pwd_buffer514_from_str(uint8_t buffer[514],
+ const char *password,
+ uint32_t string_flags)
+{
+ ssize_t pw_len;
+
+ pw_len = _encode_pwd_buffer_from_str(buffer + 2,
+ password,
+ string_flags,
+ ENCODE_ORDER_PASSWORD_FIRST);
+ if (pw_len < 0) {
+ return false;
+ }
+
+ PUSH_LE_U16(buffer, 0, pw_len);
+
+ return true;
+}
+
+bool extract_pwd_blob_from_buffer514(TALLOC_CTX *mem_ctx,
+ const uint8_t in_buffer[514],
+ DATA_BLOB *new_password)
+{
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("in_buffer: "));
+ dump_data(100, in_buffer, 514);
+#endif
+
+ new_password->length = PULL_LE_U16(in_buffer, 0);
+ if (new_password->length == 0 || new_password->length > 512) {
+ return false;
+ }
+
+ new_password->data =
+ talloc_memdup(mem_ctx, in_buffer + 2, new_password->length);
+ if (new_password->data == NULL) {
+ return false;
+ }
+ talloc_keep_secret(new_password->data);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("new_pwd_len: %zu\n", new_password->length));
+ DEBUG(100, ("new_pwd: "));
+ dump_data(100, new_password->data, new_password->length);
+#endif
+
+ return true;
+}
+
+bool decode_pwd_string_from_buffer514(TALLOC_CTX *mem_ctx,
+ const uint8_t in_buffer[514],
+ charset_t string_charset,
+ DATA_BLOB *decoded_password)
+{
+ DATA_BLOB new_password = {
+ .length = 0,
+ };
+ bool ok;
+
+ ok = extract_pwd_blob_from_buffer514(mem_ctx, in_buffer, &new_password);
+ if (!ok) {
+ return false;
+ }
+
+ ok = convert_string_talloc(mem_ctx,
+ string_charset,
+ CH_UNIX,
+ new_password.data,
+ new_password.length,
+ (void *)&decoded_password->data,
+ &decoded_password->length);
+ data_blob_free(&new_password);
+ if (!ok) {
+ return false;
+ }
+ talloc_keep_secret(decoded_password->data);
+
+ return true;
+}
+
+/***********************************************************
+ Encode an arc4 password change buffer.
+************************************************************/
+NTSTATUS encode_rc4_passwd_buffer(const char *passwd,
+ const DATA_BLOB *session_key,
+ struct samr_CryptPasswordEx *out_crypt_pwd)
+{
+ uint8_t _confounder[16] = {0};
+ DATA_BLOB confounder = data_blob_const(_confounder, 16);
+ DATA_BLOB pw_data = data_blob_const(out_crypt_pwd->data, 516);
+ bool ok;
+ int rc;
+
+ ok = encode_pw_buffer(pw_data.data, passwd, STR_UNICODE);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ generate_random_buffer(confounder.data, confounder.length);
+
+ rc = samba_gnutls_arcfour_confounded_md5(&confounder,
+ session_key,
+ &pw_data,
+ SAMBA_GNUTLS_ENCRYPT);
+ if (rc < 0) {
+ ZERO_ARRAY(_confounder);
+ data_blob_clear(&pw_data);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ /*
+ * The packet format is the 516 byte RC4 encrypted
+ * pasword followed by the 16 byte counfounder
+ * The confounder is a salt to prevent pre-computed hash attacks on the
+ * database.
+ */
+ memcpy(&out_crypt_pwd->data[516], confounder.data, confounder.length);
+ ZERO_ARRAY(_confounder);
+
+ return NT_STATUS_OK;
+}
+
+/***********************************************************
+ Decode an arc4 encrypted password change buffer.
+************************************************************/
+
+NTSTATUS decode_rc4_passwd_buffer(const DATA_BLOB *psession_key,
+ struct samr_CryptPasswordEx *inout_crypt_pwd)
+{
+ /* Confounder is last 16 bytes. */
+ DATA_BLOB confounder = data_blob_const(&inout_crypt_pwd->data[516], 16);
+ DATA_BLOB pw_data = data_blob_const(&inout_crypt_pwd->data, 516);
+ int rc;
+
+ rc = samba_gnutls_arcfour_confounded_md5(&confounder,
+ psession_key,
+ &pw_data,
+ SAMBA_GNUTLS_DECRYPT);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***********************************************************
+ encode a password buffer with an already unicode password. The
+ rest of the buffer is filled with random data to make it harder to attack.
+************************************************************/
+
+static bool create_pw_buffer_from_blob(uint8_t buffer[512],
+ const DATA_BLOB *in_password,
+ enum encode_order order)
+{
+ size_t pwd_pos = 0;
+ size_t random_pos = 0;
+ size_t random_len = 0;
+
+ if (in_password->length > 512) {
+ return false;
+ }
+
+ switch (order) {
+ case ENCODE_ORDER_PASSWORD_FIRST:
+ pwd_pos = 0;
+ random_pos = in_password->length;
+ break;
+ case ENCODE_ORDER_PASSWORD_LAST:
+ pwd_pos = PASSWORD_BUFFER_LEN - in_password->length;
+ random_pos = 0;
+ break;
+ }
+ random_len = PASSWORD_BUFFER_LEN - in_password->length;
+
+ memcpy(buffer + pwd_pos, in_password->data, in_password->length);
+ generate_random_buffer(buffer + random_pos, random_len);
+
+ return true;
+}
+
+bool set_pw_in_buffer(uint8_t buffer[516], const DATA_BLOB *password)
+{
+ bool ok;
+
+ ok = create_pw_buffer_from_blob(buffer,
+ password,
+ ENCODE_ORDER_PASSWORD_LAST);
+ if (!ok) {
+ return false;
+ }
+
+ /*
+ * The length of the new password is in the last 4 bytes of
+ * the data buffer.
+ */
+ PUSH_LE_U32(buffer, PASSWORD_BUFFER_LEN, password->length);
+
+ return true;
+}
+
+/***********************************************************
+ decode a password buffer
+ *new_pw_size is the length in bytes of the extracted unicode password
+************************************************************/
+bool extract_pw_from_buffer(TALLOC_CTX *mem_ctx,
+ uint8_t in_buffer[516], DATA_BLOB *new_pass)
+{
+ int byte_len=0;
+
+ /* The length of the new password is in the last 4 bytes of the data buffer. */
+
+ byte_len = IVAL(in_buffer, 512);
+
+#ifdef DEBUG_PASSWORD
+ dump_data(100, in_buffer, 516);
+#endif
+
+ /* Password cannot be longer than the size of the password buffer */
+ if ( (byte_len < 0) || (byte_len > 512)) {
+ return false;
+ }
+
+ *new_pass = data_blob_talloc(mem_ctx, &in_buffer[512 - byte_len], byte_len);
+
+ if (!new_pass->data) {
+ return false;
+ }
+ talloc_keep_secret(new_pass->data);
+
+ return true;
+}
+
+
+/* encode a wkssvc_PasswordBuffer:
+ *
+ * similar to samr_CryptPasswordEx. Different: 8byte confounder (instead of
+ * 16byte), confounder in front of the 516 byte buffer (instead of after that
+ * buffer), calling MD5Update() first with session_key and then with confounder
+ * (vice versa in samr) - Guenther */
+
+WERROR encode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx,
+ const char *pwd,
+ DATA_BLOB *session_key,
+ struct wkssvc_PasswordBuffer **out_pwd_buf)
+{
+ struct wkssvc_PasswordBuffer *pwd_buf = NULL;
+ uint8_t _confounder[8] = {0};
+ DATA_BLOB confounder = data_blob_const(_confounder, 8);
+ uint8_t pwbuf[516] = {0};
+ DATA_BLOB encrypt_pwbuf = data_blob_const(pwbuf, 516);
+ int rc;
+
+ pwd_buf = talloc_zero(mem_ctx, struct wkssvc_PasswordBuffer);
+ if (pwd_buf == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ encode_pw_buffer(pwbuf, pwd, STR_UNICODE);
+
+ generate_random_buffer(_confounder, sizeof(_confounder));
+
+ rc = samba_gnutls_arcfour_confounded_md5(session_key,
+ &confounder,
+ &encrypt_pwbuf,
+ SAMBA_GNUTLS_ENCRYPT);
+ if (rc < 0) {
+ ZERO_ARRAY(_confounder);
+ TALLOC_FREE(pwd_buf);
+ return gnutls_error_to_werror(rc, WERR_CONTENT_BLOCKED);
+ }
+
+ memcpy(&pwd_buf->data[0], confounder.data, confounder.length);
+ ZERO_ARRAY(_confounder);
+ memcpy(&pwd_buf->data[8], encrypt_pwbuf.data, encrypt_pwbuf.length);
+ ZERO_ARRAY(pwbuf);
+
+ *out_pwd_buf = pwd_buf;
+
+ return WERR_OK;
+}
+
+WERROR decode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx,
+ struct wkssvc_PasswordBuffer *pwd_buf,
+ DATA_BLOB *session_key,
+ char **pwd)
+{
+ uint8_t _confounder[8] = { 0 };
+ DATA_BLOB confounder = data_blob_const(_confounder, 8);
+ uint8_t pwbuf[516] = {0};
+ DATA_BLOB decrypt_pwbuf = data_blob_const(pwbuf, 516);
+ bool ok;
+ int rc;
+
+ if (pwd_buf == NULL) {
+ return WERR_INVALID_PASSWORD;
+ }
+
+ *pwd = NULL;
+
+ if (session_key->length != 16) {
+ DEBUG(10,("invalid session key\n"));
+ return WERR_INVALID_PASSWORD;
+ }
+
+ confounder = data_blob_const(&pwd_buf->data[0], 8);
+ memcpy(&pwbuf, &pwd_buf->data[8], 516);
+
+ rc = samba_gnutls_arcfour_confounded_md5(session_key,
+ &confounder,
+ &decrypt_pwbuf,
+ SAMBA_GNUTLS_ENCRYPT);
+ if (rc < 0) {
+ ZERO_ARRAY(_confounder);
+ TALLOC_FREE(pwd_buf);
+ return gnutls_error_to_werror(rc, WERR_CONTENT_BLOCKED);
+ }
+
+ ok = decode_pw_buffer(mem_ctx,
+ decrypt_pwbuf.data,
+ pwd,
+ &decrypt_pwbuf.length,
+ CH_UTF16);
+ ZERO_ARRAY(pwbuf);
+
+ if (!ok) {
+ return WERR_INVALID_PASSWORD;
+ }
+
+ return WERR_OK;
+}
diff --git a/libcli/auth/spnego.h b/libcli/auth/spnego.h
new file mode 100644
index 0000000..49645e0
--- /dev/null
+++ b/libcli/auth/spnego.h
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RFC2478 Compliant SPNEGO implementation
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define OID_SPNEGO "1.3.6.1.5.5.2"
+#define OID_NTLMSSP "1.3.6.1.4.1.311.2.2.10"
+#define OID_KERBEROS5_OLD "1.2.840.48018.1.2.2"
+#define OID_KERBEROS5 "1.2.840.113554.1.2.2"
+
+#define ADS_IGNORE_PRINCIPAL "not_defined_in_RFC4178@please_ignore"
+
+#define SPNEGO_DELEG_FLAG 0x01
+#define SPNEGO_MUTUAL_FLAG 0x02
+#define SPNEGO_REPLAY_FLAG 0x04
+#define SPNEGO_SEQUENCE_FLAG 0x08
+#define SPNEGO_ANON_FLAG 0x10
+#define SPNEGO_CONF_FLAG 0x20
+#define SPNEGO_INTEG_FLAG 0x40
+
+#define TOK_ID_KRB_AP_REQ ((const uint8_t *)"\x01\x00")
+#define TOK_ID_KRB_AP_REP ((const uint8_t *)"\x02\x00")
+#define TOK_ID_KRB_ERROR ((const uint8_t *)"\x03\x00")
+#define TOK_ID_GSS_GETMIC ((const uint8_t *)"\x01\x01")
+#define TOK_ID_GSS_WRAP ((const uint8_t *)"\x02\x01")
+
+enum spnego_negResult {
+ SPNEGO_ACCEPT_COMPLETED = 0,
+ SPNEGO_ACCEPT_INCOMPLETE = 1,
+ SPNEGO_REJECT = 2,
+ SPNEGO_REQUEST_MIC = 3,
+ /*
+ * The max value is 0xff (255) on the wire
+ */
+ SPNEGO_NONE_RESULT = 256
+};
+
+struct spnego_negTokenInit {
+ const char * const *mechTypes;
+ DATA_BLOB reqFlags;
+ uint8_t reqFlagsPadding;
+ DATA_BLOB mechToken;
+ DATA_BLOB mechListMIC;
+ char *targetPrincipal;
+};
+
+struct spnego_negTokenTarg {
+ enum spnego_negResult negResult;
+ const char *supportedMech;
+ DATA_BLOB responseToken;
+ DATA_BLOB mechListMIC;
+};
+
+struct spnego_data {
+ int type;
+ struct spnego_negTokenInit negTokenInit;
+ struct spnego_negTokenTarg negTokenTarg;
+};
+
+enum spnego_message_type {
+ SPNEGO_NEG_TOKEN_INIT = 0,
+ SPNEGO_NEG_TOKEN_TARG = 1,
+};
+
+#include "../libcli/auth/spnego_proto.h"
diff --git a/libcli/auth/spnego_parse.c b/libcli/auth/spnego_parse.c
new file mode 100644
index 0000000..f7f19b1
--- /dev/null
+++ b/libcli/auth/spnego_parse.c
@@ -0,0 +1,446 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RFC2478 Compliant SPNEGO implementation
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../libcli/auth/spnego.h"
+#include "../lib/util/asn1.h"
+
+static bool read_negTokenInit(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
+ struct spnego_negTokenInit *token)
+{
+ ZERO_STRUCTP(token);
+
+ if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
+ if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
+
+ while (asn1_tag_remaining(asn1) > 0) {
+ int i;
+ uint8_t context;
+
+ if (!asn1_peek_uint8(asn1, &context)) {
+ asn1_set_error(asn1);
+ break;
+ }
+
+ switch (context) {
+ /* Read mechTypes */
+ case ASN1_CONTEXT(0): {
+ const char **mechTypes;
+
+ if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
+ if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
+
+ mechTypes = talloc(mem_ctx, const char *);
+ if (mechTypes == NULL) {
+ asn1_set_error(asn1);
+ return false;
+ }
+ for (i = 0; asn1_tag_remaining(asn1) > 0; i++) {
+ char *oid;
+ const char **p;
+ p = talloc_realloc(mem_ctx,
+ mechTypes,
+ const char *, i+2);
+ if (p == NULL) {
+ talloc_free(mechTypes);
+ asn1_set_error(asn1);
+ return false;
+ }
+ mechTypes = p;
+
+ if (!asn1_read_OID(asn1, mechTypes, &oid)) return false;
+ mechTypes[i] = oid;
+ }
+ mechTypes[i] = NULL;
+ token->mechTypes = mechTypes;
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+ break;
+ }
+ /* Read reqFlags */
+ case ASN1_CONTEXT(1):
+ if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
+ if (!asn1_read_BitString(asn1, mem_ctx, &token->reqFlags,
+ &token->reqFlagsPadding)) return false;
+ if (!asn1_end_tag(asn1)) return false;
+ break;
+ /* Read mechToken */
+ case ASN1_CONTEXT(2):
+ if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false;
+ if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechToken)) return false;
+ if (!asn1_end_tag(asn1)) return false;
+ break;
+ /* Read mecListMIC */
+ case ASN1_CONTEXT(3):
+ {
+ uint8_t type_peek;
+ if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false;
+ if (!asn1_peek_uint8(asn1, &type_peek)) {
+ asn1_set_error(asn1);
+ break;
+ }
+ if (type_peek == ASN1_OCTET_STRING) {
+ if (!asn1_read_OctetString(asn1, mem_ctx,
+ &token->mechListMIC)) return false;
+ } else {
+ /* RFC 2478 says we have an Octet String here,
+ but W2k sends something different... */
+ char *mechListMIC;
+ if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
+ if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
+ if (!asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC)) return false;
+ if (!asn1_end_tag(asn1)) return false;
+ if (!asn1_end_tag(asn1)) return false;
+
+ token->targetPrincipal = mechListMIC;
+ }
+ if (!asn1_end_tag(asn1)) return false;
+ break;
+ }
+ default:
+ asn1_set_error(asn1);
+ break;
+ }
+ }
+
+ if (!asn1_end_tag(asn1)) return false;
+ if (!asn1_end_tag(asn1)) return false;
+
+ return !asn1_has_error(asn1);
+}
+
+static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
+{
+ if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
+ if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
+
+ /* Write mechTypes */
+ if (token->mechTypes && *token->mechTypes) {
+ int i;
+
+ if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
+ if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
+ for (i = 0; token->mechTypes[i]; i++) {
+ if (!asn1_write_OID(asn1, token->mechTypes[i])) return false;
+ }
+ if (!asn1_pop_tag(asn1)) return false;
+ if (!asn1_pop_tag(asn1)) return false;
+ }
+
+ /* write reqFlags */
+ if (token->reqFlags.length > 0) {
+ if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
+ if (!asn1_write_BitString(asn1, token->reqFlags.data,
+ token->reqFlags.length,
+ token->reqFlagsPadding)) return false;
+ if (!asn1_pop_tag(asn1)) return false;
+ }
+
+ /* write mechToken */
+ if (token->mechToken.data) {
+ if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false;
+ if (!asn1_write_OctetString(asn1, token->mechToken.data,
+ token->mechToken.length)) return false;
+ if (!asn1_pop_tag(asn1)) return false;
+ }
+
+ /* write mechListMIC */
+ if (token->mechListMIC.data) {
+ if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false;
+#if 0
+ /* This is what RFC 2478 says ... */
+ asn1_write_OctetString(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+#else
+ /* ... but unfortunately this is what Windows
+ sends/expects */
+ if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
+ if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
+ if (!asn1_push_tag(asn1, ASN1_GENERAL_STRING)) return false;
+ if (!asn1_write(asn1, token->mechListMIC.data,
+ token->mechListMIC.length)) return false;
+ if (!asn1_pop_tag(asn1)) return false;
+ if (!asn1_pop_tag(asn1)) return false;
+ if (!asn1_pop_tag(asn1)) return false;
+#endif
+ if (!asn1_pop_tag(asn1)) return false;
+ }
+
+ if (!asn1_pop_tag(asn1)) return false;
+ if (!asn1_pop_tag(asn1)) return false;
+
+ return !asn1_has_error(asn1);
+}
+
+static bool read_negTokenTarg(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
+ struct spnego_negTokenTarg *token)
+{
+ ZERO_STRUCTP(token);
+
+ if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
+ if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
+
+ while (asn1_tag_remaining(asn1) > 0) {
+ uint8_t context;
+ uint8_t neg_result;
+ char *oid;
+
+ if (!asn1_peek_uint8(asn1, &context)) {
+ asn1_set_error(asn1);
+ break;
+ }
+
+ switch (context) {
+ case ASN1_CONTEXT(0):
+ if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
+ if (!asn1_start_tag(asn1, ASN1_ENUMERATED)) return false;
+ if (!asn1_read_uint8(asn1, &neg_result)) return false;
+ token->negResult = neg_result;
+ if (!asn1_end_tag(asn1)) return false;
+ if (!asn1_end_tag(asn1)) return false;
+ break;
+ case ASN1_CONTEXT(1):
+ if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
+ if (!asn1_read_OID(asn1, mem_ctx, &oid)) return false;
+ token->supportedMech = oid;
+ if (!asn1_end_tag(asn1)) return false;
+ break;
+ case ASN1_CONTEXT(2):
+ if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false;
+ if (!asn1_read_OctetString(asn1, mem_ctx, &token->responseToken)) return false;
+ if (!asn1_end_tag(asn1)) return false;
+ break;
+ case ASN1_CONTEXT(3):
+ if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false;
+ if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC)) return false;
+ if (!asn1_end_tag(asn1)) return false;
+ break;
+ default:
+ asn1_set_error(asn1);
+ break;
+ }
+ }
+
+ if (!asn1_end_tag(asn1)) return false;
+ if (!asn1_end_tag(asn1)) return false;
+
+ return !asn1_has_error(asn1);
+}
+
+static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
+{
+ if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
+ if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
+
+ if (token->negResult != SPNEGO_NONE_RESULT) {
+ if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
+ if (!asn1_write_enumerated(asn1, token->negResult)) return false;
+ if (!asn1_pop_tag(asn1)) return false;
+ }
+
+ if (token->supportedMech) {
+ if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
+ if (!asn1_write_OID(asn1, token->supportedMech)) return false;
+ if (!asn1_pop_tag(asn1)) return false;
+ }
+
+ if (token->responseToken.data) {
+ if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false;
+ if (!asn1_write_OctetString(asn1, token->responseToken.data,
+ token->responseToken.length)) return false;
+ if (!asn1_pop_tag(asn1)) return false;
+ }
+
+ if (token->mechListMIC.data) {
+ if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false;
+ if (!asn1_write_OctetString(asn1, token->mechListMIC.data,
+ token->mechListMIC.length)) return false;
+ if (!asn1_pop_tag(asn1)) return false;
+ }
+
+ if (!asn1_pop_tag(asn1)) return false;
+ if (!asn1_pop_tag(asn1)) return false;
+
+ return !asn1_has_error(asn1);
+}
+
+ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token)
+{
+ struct asn1_data *asn1;
+ ssize_t ret = -1;
+ uint8_t context;
+
+ ZERO_STRUCTP(token);
+
+ if (data.length == 0) {
+ return ret;
+ }
+
+ asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ if (asn1 == NULL) {
+ return -1;
+ }
+
+ if (!asn1_load(asn1, data)) goto err;
+
+ if (!asn1_peek_uint8(asn1, &context)) {
+ asn1_set_error(asn1);
+ } else {
+ switch (context) {
+ case ASN1_APPLICATION(0):
+ if (!asn1_start_tag(asn1, ASN1_APPLICATION(0))) goto err;
+ if (!asn1_check_OID(asn1, OID_SPNEGO)) goto err;
+ if (read_negTokenInit(asn1, mem_ctx, &token->negTokenInit)) {
+ token->type = SPNEGO_NEG_TOKEN_INIT;
+ }
+ if (!asn1_end_tag(asn1)) goto err;
+ break;
+ case ASN1_CONTEXT(1):
+ if (read_negTokenTarg(asn1, mem_ctx, &token->negTokenTarg)) {
+ token->type = SPNEGO_NEG_TOKEN_TARG;
+ }
+ break;
+ default:
+ asn1_set_error(asn1);
+ break;
+ }
+ }
+
+ if (!asn1_has_error(asn1)) {
+ ret = asn1_current_ofs(asn1);
+ }
+
+ err:
+
+ asn1_free(asn1);
+
+ return ret;
+}
+
+ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
+{
+ struct asn1_data *asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ ssize_t ret = -1;
+
+ if (asn1 == NULL) {
+ return -1;
+ }
+
+ switch (spnego->type) {
+ case SPNEGO_NEG_TOKEN_INIT:
+ if (!asn1_push_tag(asn1, ASN1_APPLICATION(0))) goto err;
+ if (!asn1_write_OID(asn1, OID_SPNEGO)) goto err;
+ if (!write_negTokenInit(asn1, &spnego->negTokenInit)) goto err;
+ if (!asn1_pop_tag(asn1)) goto err;
+ break;
+ case SPNEGO_NEG_TOKEN_TARG:
+ write_negTokenTarg(asn1, &spnego->negTokenTarg);
+ break;
+ default:
+ asn1_set_error(asn1);
+ break;
+ }
+
+ if (!asn1_extract_blob(asn1, mem_ctx, blob)) {
+ goto err;
+ }
+
+ ret = asn1_current_ofs(asn1);
+
+ err:
+
+ asn1_free(asn1);
+
+ return ret;
+}
+
+bool spnego_free_data(struct spnego_data *spnego)
+{
+ bool ret = true;
+
+ if (!spnego) goto out;
+
+ switch(spnego->type) {
+ case SPNEGO_NEG_TOKEN_INIT:
+ if (spnego->negTokenInit.mechTypes) {
+ talloc_free(discard_const(spnego->negTokenInit.mechTypes));
+ }
+ data_blob_free(&spnego->negTokenInit.reqFlags);
+ data_blob_free(&spnego->negTokenInit.mechToken);
+ data_blob_free(&spnego->negTokenInit.mechListMIC);
+ talloc_free(spnego->negTokenInit.targetPrincipal);
+ break;
+ case SPNEGO_NEG_TOKEN_TARG:
+ if (spnego->negTokenTarg.supportedMech) {
+ talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
+ }
+ data_blob_free(&spnego->negTokenTarg.responseToken);
+ data_blob_free(&spnego->negTokenTarg.mechListMIC);
+ break;
+ default:
+ ret = false;
+ break;
+ }
+ ZERO_STRUCTP(spnego);
+out:
+ return ret;
+}
+
+bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
+ const char * const *mech_types,
+ DATA_BLOB *blob)
+{
+ bool ret = false;
+ struct asn1_data *asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+
+ if (asn1 == NULL) {
+ return false;
+ }
+
+ /* Write mechTypes */
+ if (mech_types && *mech_types) {
+ int i;
+
+ if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) goto err;
+ for (i = 0; mech_types[i]; i++) {
+ if (!asn1_write_OID(asn1, mech_types[i])) goto err;
+ }
+ if (!asn1_pop_tag(asn1)) goto err;
+ }
+
+ if (asn1_has_error(asn1)) {
+ goto err;
+ }
+
+ if (!asn1_extract_blob(asn1, mem_ctx, blob)) {
+ goto err;
+ }
+
+ ret = true;
+
+ err:
+
+ asn1_free(asn1);
+
+ return ret;
+}
diff --git a/libcli/auth/spnego_proto.h b/libcli/auth/spnego_proto.h
new file mode 100644
index 0000000..c0fa934
--- /dev/null
+++ b/libcli/auth/spnego_proto.h
@@ -0,0 +1,28 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RFC2478 Compliant SPNEGO implementation
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token);
+ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego);
+bool spnego_free_data(struct spnego_data *spnego);
+bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
+ const char * const *mech_types,
+ DATA_BLOB *blob);
diff --git a/libcli/auth/tests/ntlm_check.c b/libcli/auth/tests/ntlm_check.c
new file mode 100644
index 0000000..79b8ec9
--- /dev/null
+++ b/libcli/auth/tests/ntlm_check.c
@@ -0,0 +1,413 @@
+/*
+ * Unit tests for the ntlm_check password hash check library.
+ *
+ * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * from cmocka.c:
+ * These headers or their equivalents should be included prior to
+ * including
+ * this header file.
+ *
+ * #include <stdarg.h>
+ * #include <stddef.h>
+ * #include <setjmp.h>
+ *
+ * This allows test applications to use custom definitions of C standard
+ * library functions and types.
+ *
+ */
+
+/*
+ * Note that the messaging routines (audit_message_send and get_event_server)
+ * are not tested by these unit tests. Currently they are for integration
+ * test support, and as such are exercised by the integration tests.
+ */
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "includes.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/credentials/credentials.h"
+
+struct ntlm_state {
+ const char *username;
+ const char *domain;
+ DATA_BLOB challenge;
+ DATA_BLOB ntlm;
+ DATA_BLOB lm;
+ DATA_BLOB ntlm_key;
+ DATA_BLOB lm_key;
+ const struct samr_Password *nt_hash;
+};
+
+static int test_ntlm_setup_with_options(void **state,
+ int flags, bool upn)
+{
+ NTSTATUS status;
+ DATA_BLOB challenge = {
+ .data = discard_const_p(uint8_t, "I am a teapot"),
+ .length = 8
+ };
+ struct ntlm_state *ntlm_state = talloc(NULL, struct ntlm_state);
+ DATA_BLOB target_info = NTLMv2_generate_names_blob(ntlm_state,
+ NULL,
+ "serverdom");
+ struct cli_credentials *creds = cli_credentials_init(ntlm_state);
+ cli_credentials_set_username(creds,
+ "testuser",
+ CRED_SPECIFIED);
+ cli_credentials_set_domain(creds,
+ "testdom",
+ CRED_SPECIFIED);
+ cli_credentials_set_workstation(creds,
+ "testwksta",
+ CRED_SPECIFIED);
+ cli_credentials_set_password(creds,
+ "testpass",
+ CRED_SPECIFIED);
+
+ if (upn) {
+ cli_credentials_set_principal(creds,
+ "testuser@samba.org",
+ CRED_SPECIFIED);
+ }
+
+ cli_credentials_get_ntlm_username_domain(creds,
+ ntlm_state,
+ &ntlm_state->username,
+ &ntlm_state->domain);
+
+ status = cli_credentials_get_ntlm_response(creds,
+ ntlm_state,
+ &flags,
+ challenge,
+ NULL,
+ target_info,
+ &ntlm_state->lm,
+ &ntlm_state->ntlm,
+ &ntlm_state->lm_key,
+ &ntlm_state->ntlm_key);
+ ntlm_state->challenge = challenge;
+
+ ntlm_state->nt_hash = cli_credentials_get_nt_hash(creds,
+ ntlm_state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ *state = ntlm_state;
+ return 0;
+}
+
+static int test_ntlm_setup(void **state) {
+ return test_ntlm_setup_with_options(state, 0, false);
+}
+
+static int test_ntlm_and_lm_setup(void **state) {
+ return test_ntlm_setup_with_options(state,
+ CLI_CRED_LANMAN_AUTH,
+ false);
+}
+
+static int test_ntlm2_setup(void **state) {
+ return test_ntlm_setup_with_options(state,
+ CLI_CRED_NTLM2,
+ false);
+}
+
+static int test_ntlmv2_setup(void **state) {
+ return test_ntlm_setup_with_options(state,
+ CLI_CRED_NTLMv2_AUTH,
+ false);
+}
+
+static int test_ntlm_teardown(void **state)
+{
+ struct ntlm_state *ntlm_state
+ = talloc_get_type_abort(*state,
+ struct ntlm_state);
+ TALLOC_FREE(ntlm_state);
+ *state = NULL;
+ return 0;
+}
+
+static void test_ntlm_allowed(void **state)
+{
+ DATA_BLOB user_sess_key, lm_sess_key;
+ struct ntlm_state *ntlm_state
+ = talloc_get_type_abort(*state,
+ struct ntlm_state);
+ NTSTATUS status;
+ status = ntlm_password_check(ntlm_state,
+ false,
+ NTLM_AUTH_ON,
+ 0,
+ &ntlm_state->challenge,
+ &ntlm_state->lm,
+ &ntlm_state->ntlm,
+ ntlm_state->username,
+ ntlm_state->username,
+ ntlm_state->domain,
+ NULL,
+ ntlm_state->nt_hash,
+ &user_sess_key,
+ &lm_sess_key);
+
+ assert_int_equal(NT_STATUS_V(status), NT_STATUS_V(NT_STATUS_OK));
+}
+
+static void test_ntlm_allowed_lm_supplied(void **state)
+{
+ test_ntlm_allowed(state);
+}
+
+static void test_ntlm_disabled(void **state)
+{
+ DATA_BLOB user_sess_key, lm_sess_key;
+ struct ntlm_state *ntlm_state
+ = talloc_get_type_abort(*state,
+ struct ntlm_state);
+ NTSTATUS status;
+ status = ntlm_password_check(ntlm_state,
+ false,
+ NTLM_AUTH_DISABLED,
+ 0,
+ &ntlm_state->challenge,
+ &ntlm_state->lm,
+ &ntlm_state->ntlm,
+ ntlm_state->username,
+ ntlm_state->username,
+ ntlm_state->domain,
+ NULL,
+ ntlm_state->nt_hash,
+ &user_sess_key,
+ &lm_sess_key);
+
+ assert_int_equal(NT_STATUS_V(status), NT_STATUS_V(NT_STATUS_NTLM_BLOCKED));
+}
+
+static void test_ntlm2(void **state)
+{
+ DATA_BLOB user_sess_key, lm_sess_key;
+ struct ntlm_state *ntlm_state
+ = talloc_get_type_abort(*state,
+ struct ntlm_state);
+ NTSTATUS status;
+ status = ntlm_password_check(ntlm_state,
+ false,
+ NTLM_AUTH_ON,
+ 0,
+ &ntlm_state->challenge,
+ &ntlm_state->lm,
+ &ntlm_state->ntlm,
+ ntlm_state->username,
+ ntlm_state->username,
+ ntlm_state->domain,
+ NULL,
+ ntlm_state->nt_hash,
+ &user_sess_key,
+ &lm_sess_key);
+
+ /*
+ * NTLM2 session security (where the real challenge is the
+ * MD5(challenge, client-challenge) (in the first 8 bytes of
+ * the lm) isn't decoded by ntlm_password_check(), it must
+ * first be converted back into normal NTLM by the NTLMSSP
+ * layer
+ */
+ assert_int_equal(NT_STATUS_V(status),
+ NT_STATUS_V(NT_STATUS_WRONG_PASSWORD));
+}
+
+static void test_ntlm_mschapv2_only_allowed(void **state)
+{
+ DATA_BLOB user_sess_key, lm_sess_key;
+ struct ntlm_state *ntlm_state
+ = talloc_get_type_abort(*state,
+ struct ntlm_state);
+ NTSTATUS status;
+ status = ntlm_password_check(ntlm_state,
+ false,
+ NTLM_AUTH_MSCHAPv2_NTLMV2_ONLY,
+ MSV1_0_ALLOW_MSVCHAPV2,
+ &ntlm_state->challenge,
+ &ntlm_state->lm,
+ &ntlm_state->ntlm,
+ ntlm_state->username,
+ ntlm_state->username,
+ ntlm_state->domain,
+ NULL,
+ ntlm_state->nt_hash,
+ &user_sess_key,
+ &lm_sess_key);
+
+ assert_int_equal(NT_STATUS_V(status), NT_STATUS_V(NT_STATUS_OK));
+}
+
+static void test_ntlm_mschapv2_only_denied(void **state)
+{
+ DATA_BLOB user_sess_key, lm_sess_key;
+ struct ntlm_state *ntlm_state
+ = talloc_get_type_abort(*state,
+ struct ntlm_state);
+ NTSTATUS status;
+ status = ntlm_password_check(ntlm_state,
+ false,
+ NTLM_AUTH_MSCHAPv2_NTLMV2_ONLY,
+ 0,
+ &ntlm_state->challenge,
+ &ntlm_state->lm,
+ &ntlm_state->ntlm,
+ ntlm_state->username,
+ ntlm_state->username,
+ ntlm_state->domain,
+ NULL,
+ ntlm_state->nt_hash,
+ &user_sess_key,
+ &lm_sess_key);
+
+ assert_int_equal(NT_STATUS_V(status),
+ NT_STATUS_V(NT_STATUS_WRONG_PASSWORD));
+}
+
+static void test_ntlmv2_only_ntlmv2(void **state)
+{
+ DATA_BLOB user_sess_key, lm_sess_key;
+ struct ntlm_state *ntlm_state
+ = talloc_get_type_abort(*state,
+ struct ntlm_state);
+ NTSTATUS status;
+ status = ntlm_password_check(ntlm_state,
+ false,
+ NTLM_AUTH_NTLMV2_ONLY,
+ 0,
+ &ntlm_state->challenge,
+ &ntlm_state->lm,
+ &ntlm_state->ntlm,
+ ntlm_state->username,
+ ntlm_state->username,
+ ntlm_state->domain,
+ NULL,
+ ntlm_state->nt_hash,
+ &user_sess_key,
+ &lm_sess_key);
+
+ assert_int_equal(NT_STATUS_V(status), NT_STATUS_V(NT_STATUS_OK));
+}
+
+static void test_ntlmv2_only_ntlm(void **state)
+{
+ DATA_BLOB user_sess_key, lm_sess_key;
+ struct ntlm_state *ntlm_state
+ = talloc_get_type_abort(*state,
+ struct ntlm_state);
+ NTSTATUS status;
+ status = ntlm_password_check(ntlm_state,
+ false,
+ NTLM_AUTH_NTLMV2_ONLY,
+ 0,
+ &ntlm_state->challenge,
+ &ntlm_state->lm,
+ &ntlm_state->ntlm,
+ ntlm_state->username,
+ ntlm_state->username,
+ ntlm_state->domain,
+ NULL,
+ ntlm_state->nt_hash,
+ &user_sess_key,
+ &lm_sess_key);
+
+ assert_int_equal(NT_STATUS_V(status),
+ NT_STATUS_V(NT_STATUS_WRONG_PASSWORD));
+}
+
+static void test_ntlmv2_only_ntlm_and_lanman(void **state)
+{
+ test_ntlmv2_only_ntlm(state);
+}
+
+static void test_ntlmv2_only_ntlm_once(void **state)
+{
+ DATA_BLOB user_sess_key, lm_sess_key;
+ struct ntlm_state *ntlm_state
+ = talloc_get_type_abort(*state,
+ struct ntlm_state);
+ NTSTATUS status;
+ status = ntlm_password_check(ntlm_state,
+ false,
+ NTLM_AUTH_NTLMV2_ONLY,
+ 0,
+ &ntlm_state->challenge,
+ &data_blob_null,
+ &ntlm_state->ntlm,
+ ntlm_state->username,
+ ntlm_state->username,
+ ntlm_state->domain,
+ NULL,
+ ntlm_state->nt_hash,
+ &user_sess_key,
+ &lm_sess_key);
+
+ assert_int_equal(NT_STATUS_V(status),
+ NT_STATUS_V(NT_STATUS_WRONG_PASSWORD));
+}
+
+int main(int argc, const char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(test_ntlm_allowed,
+ test_ntlm_setup,
+ test_ntlm_teardown),
+ cmocka_unit_test_setup_teardown(test_ntlm_allowed_lm_supplied,
+ test_ntlm_and_lm_setup,
+ test_ntlm_teardown),
+ cmocka_unit_test_setup_teardown(test_ntlm_disabled,
+ test_ntlm_setup,
+ test_ntlm_teardown),
+ cmocka_unit_test_setup_teardown(test_ntlm2,
+ test_ntlm2_setup,
+ test_ntlm_teardown),
+ cmocka_unit_test_setup_teardown(test_ntlm_mschapv2_only_allowed,
+ test_ntlm_setup,
+ test_ntlm_teardown),
+ cmocka_unit_test_setup_teardown(test_ntlm_mschapv2_only_denied,
+ test_ntlm_setup,
+ test_ntlm_teardown),
+ cmocka_unit_test_setup_teardown(test_ntlmv2_only_ntlm,
+ test_ntlm_setup,
+ test_ntlm_teardown),
+ cmocka_unit_test_setup_teardown(test_ntlmv2_only_ntlm_and_lanman,
+ test_ntlm_and_lm_setup,
+ test_ntlm_teardown),
+ cmocka_unit_test_setup_teardown(test_ntlmv2_only_ntlm_once,
+ test_ntlm_setup,
+ test_ntlm_teardown),
+ cmocka_unit_test_setup_teardown(test_ntlmv2_only_ntlmv2,
+ test_ntlmv2_setup,
+ test_ntlm_teardown)
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/libcli/auth/tests/test_encode_decode.c b/libcli/auth/tests/test_encode_decode.c
new file mode 100644
index 0000000..4683edf
--- /dev/null
+++ b/libcli/auth/tests/test_encode_decode.c
@@ -0,0 +1,162 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2022 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "libcli/auth/smbencrypt.c"
+
+/* The following hexdumps are from a Windows Server 2022 time trace */
+static const uint8_t plaintext_data[] = {
+ 0x14, 0x00, 0x50, 0x00, 0x61, 0x00, 0x24, 0x00,
+ 0x24, 0x00, 0x77, 0x00, 0x30, 0x00, 0x72, 0x00,
+ 0x64, 0x00, 0x40, 0x00, 0x32, 0x00, 0xc2, 0x34,
+ 0x7d, 0x21, 0x79, 0x05, 0xef, 0x88, 0xd7, 0x11,
+ 0xec, 0xe2, 0xce, 0xb5, 0xd4, 0x4d, 0x64, 0x2d,
+ 0x15, 0x79, 0x01, 0x39, 0xb8, 0xb9, 0x89, 0x5c,
+ 0x4e, 0x71, 0xbd, 0xf0, 0x14, 0x0c, 0x87, 0x72,
+ 0xa5, 0xfa, 0x90, 0xbe, 0x62, 0x55, 0xad, 0x7f,
+ 0xe9, 0x7f, 0x0d, 0x20, 0x19, 0x3a, 0x76, 0xbe,
+ 0xb2, 0x14, 0x6d, 0x5b, 0x25, 0x1c, 0x67, 0x3a,
+ 0x23, 0x45, 0x1f, 0x7e, 0x36, 0xa0, 0x95, 0xb7,
+ 0xa7, 0xb1, 0x33, 0xe1, 0xc4, 0xb6, 0xe6, 0x2d,
+ 0xd8, 0x2f, 0xe7, 0xdf, 0x01, 0xe8, 0xba, 0x02,
+ 0x54, 0x36, 0xe9, 0xb6, 0x5e, 0x00, 0x52, 0x9e,
+ 0x64, 0x00, 0xcb, 0x3c, 0x6d, 0x05, 0x43, 0x7d,
+ 0x01, 0x9c, 0x22, 0x18, 0x92, 0xe7, 0xa3, 0x55,
+ 0x65, 0x6d, 0x2e, 0xa3, 0x53, 0x6e, 0xc0, 0x67,
+ 0x26, 0xac, 0xaa, 0x98, 0xa4, 0xcb, 0xb4, 0x49,
+ 0x13, 0x60, 0xd4, 0x33, 0x2c, 0x77, 0x58, 0x5e,
+ 0x50, 0x45, 0xaa, 0x1e, 0x05, 0x15, 0x18, 0x59,
+ 0x55, 0xca, 0x14, 0x37, 0x31, 0xac, 0x63, 0xfc,
+ 0x63, 0xa8, 0x2a, 0xa9, 0x99, 0xec, 0x49, 0x87,
+ 0x64, 0x1d, 0x4e, 0xdd, 0xa3, 0xd0, 0xdc, 0x08,
+ 0x00, 0x17, 0xf4, 0x2f, 0x9c, 0x4a, 0x17, 0xc7,
+ 0xbd, 0x30, 0xb7, 0x0e, 0x81, 0xe4, 0xd5, 0x94,
+ 0x31, 0xff, 0xd6, 0xcc, 0xc6, 0xbb, 0x39, 0xcd,
+ 0x72, 0xfe, 0xa6, 0x3d, 0x0d, 0x88, 0x68, 0x40,
+ 0xf8, 0x51, 0x2b, 0xe6, 0xc9, 0xaa, 0x84, 0xf3,
+ 0xf4, 0x6e, 0x55, 0x37, 0xbf, 0x5d, 0x87, 0xce,
+ 0xa6, 0x80, 0x4f, 0x8f, 0x8f, 0x7b, 0xe8, 0x30,
+ 0xc3, 0x2e, 0x24, 0xc7, 0x3e, 0xf1, 0x9f, 0xa6,
+ 0x77, 0xca, 0x04, 0xbe, 0xb5, 0xe1, 0x40, 0x59,
+ 0x43, 0xc5, 0x30, 0xc8, 0xe7, 0xbf, 0xab, 0xfa,
+ 0x86, 0x62, 0xd9, 0x3a, 0x8e, 0xa9, 0x34, 0x73,
+ 0x20, 0x7b, 0x61, 0x1b, 0x0e, 0xca, 0x98, 0xec,
+ 0xa1, 0xc1, 0x78, 0xa9, 0xa7, 0x6c, 0x8c, 0xe3,
+ 0x21, 0x7d, 0xb9, 0x90, 0xe2, 0x73, 0x1a, 0x99,
+ 0x1d, 0x44, 0xa8, 0xd5, 0x7f, 0x0a, 0x59, 0x47,
+ 0xd0, 0xf5, 0x6c, 0x14, 0xff, 0x4a, 0x29, 0x20,
+ 0xb5, 0xfc, 0xe9, 0xf0, 0xa5, 0x35, 0x9e, 0x1c,
+ 0xa1, 0x4c, 0xec, 0xb5, 0x7d, 0x2d, 0x27, 0xff,
+ 0x7a, 0x42, 0x18, 0xb8, 0x53, 0x4e, 0xfb, 0xec,
+ 0xb1, 0xc1, 0x65, 0x2d, 0xa4, 0x69, 0x85, 0x56,
+ 0x61, 0x6d, 0x21, 0x66, 0x88, 0x31, 0xdf, 0xba,
+ 0x28, 0xc6, 0x9a, 0xf8, 0xb7, 0xf6, 0x2a, 0x43,
+ 0xba, 0x9b, 0x84, 0x14, 0xce, 0xa9, 0xc9, 0xf5,
+ 0x85, 0x6f, 0x31, 0x89, 0x8d, 0xfc, 0x25, 0x2e,
+ 0x98, 0x25, 0x5a, 0x03, 0xf0, 0xb8, 0x5d, 0x4a,
+ 0xd4, 0x4c, 0xc8, 0x62, 0x4e, 0xeb, 0x07, 0xc8,
+ 0x5c, 0x9e, 0x63, 0x30, 0xfe, 0x9f, 0x0f, 0x96,
+ 0xd0, 0xd7, 0x70, 0xad, 0xcd, 0x84, 0xbc, 0x1e,
+ 0x48, 0xa0, 0x20, 0x14, 0x10, 0xa4, 0xb1, 0x5b,
+ 0x05, 0x36, 0x9a, 0x6d, 0xb0, 0x10, 0x98, 0xbd,
+ 0x8d, 0xa2, 0xd1, 0xb2, 0xfa, 0x23, 0x37, 0xeb,
+ 0xb0, 0x04, 0x53, 0xcb, 0xa1, 0xa9, 0xc4, 0x88,
+ 0xdd, 0xf9, 0x80, 0xf5, 0xa9, 0xcd, 0x7b, 0xf8,
+ 0x77, 0x0b, 0x19, 0x84, 0x4c, 0xef, 0x2c, 0x14,
+ 0xa1, 0xdc, 0x9f, 0x2f, 0x41, 0xd0, 0xd0, 0x33,
+ 0x29, 0x8a, 0xb9, 0x39, 0x7e, 0xc9, 0x7f, 0xe7,
+ 0x63, 0x64, 0xa4, 0x7b, 0x4a, 0x6a, 0x33, 0xa7,
+ 0xaa, 0xf6, 0xca, 0x98, 0xc4, 0x9b, 0x62, 0x5b,
+ 0xcd, 0x53, 0x82, 0xbf, 0xf0, 0x0b, 0x9c, 0xe7,
+ 0xb2, 0x44, 0x1b, 0x88, 0x71, 0x61, 0xa1, 0x36,
+ 0x9e, 0x7a, 0x0a, 0x3c, 0x20, 0xd8, 0xff, 0xa1,
+ 0x23, 0x66
+};
+
+static void torture_encode_pwd_buffer514_from_str(void **state)
+{
+ uint8_t data[514] = {0};
+ bool ok;
+
+ ok = encode_pwd_buffer514_from_str(data, "Pa$$w0rd@2", STR_UNICODE);
+ assert_true(ok);
+
+ /* We can only compare the first 22 bytes as the rest is random data */
+ assert_memory_equal(data, plaintext_data, 22);
+ assert_memory_not_equal(data + 22,
+ plaintext_data + 22,
+ sizeof(plaintext_data) - 22);
+}
+
+static void torture_extract_pwd_blob_from_buffer514(void **state)
+{
+ DATA_BLOB new_password = {
+ .length = 0,
+ };
+ bool ok;
+
+ ok = extract_pwd_blob_from_buffer514(NULL, plaintext_data, &new_password);
+ assert_true(ok);
+ assert_int_equal(new_password.length, 20);
+ assert_memory_equal(new_password.data,
+ plaintext_data + 2,
+ new_password.length);
+ data_blob_free(&new_password);
+}
+
+static void torture_decode_pwd_string_from_buffer514(void **state)
+{
+ DATA_BLOB decoded_password = {
+ .length = 0,
+ };
+ bool ok;
+
+ ok = decode_pwd_string_from_buffer514(NULL,
+ plaintext_data,
+ CH_UTF16,
+ &decoded_password);
+ assert_true(ok);
+ assert_memory_equal(decoded_password.data, "Pa$$w0rd@2", decoded_password.length);
+ data_blob_free(&decoded_password);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(torture_encode_pwd_buffer514_from_str),
+ cmocka_unit_test(torture_extract_pwd_blob_from_buffer514),
+ cmocka_unit_test(torture_decode_pwd_string_from_buffer514),
+ };
+
+ if (argc == 2) {
+ cmocka_set_test_filter(argv[1]);
+ }
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ rc = cmocka_run_group_tests(tests, NULL, NULL);
+
+ return rc;
+}
diff --git a/libcli/auth/tests/test_gnutls.c b/libcli/auth/tests/test_gnutls.c
new file mode 100644
index 0000000..da7a3b4
--- /dev/null
+++ b/libcli/auth/tests/test_gnutls.c
@@ -0,0 +1,528 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2019 Guenther Deschner <gd@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "includes.h"
+#include "libcli/auth/libcli_auth.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#if defined(HAVE_GNUTLS_AES_CFB8) && GNUTLS_VERSION_NUMBER > 0x03060a
+static void torture_gnutls_aes_128_cfb_flags(void **state,
+ const DATA_BLOB session_key,
+ const DATA_BLOB seq_num_initial,
+ const DATA_BLOB confounder_initial,
+ const DATA_BLOB confounder_expected,
+ const DATA_BLOB clear_initial,
+ const DATA_BLOB crypt_expected)
+{
+ uint8_t confounder[8];
+ DATA_BLOB io;
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ uint8_t sess_kf0[16] = {0};
+ gnutls_datum_t key = {
+ .data = sess_kf0,
+ .size = sizeof(sess_kf0),
+ };
+ uint32_t iv_size =
+ gnutls_cipher_get_iv_size(GNUTLS_CIPHER_AES_128_CFB8);
+ uint8_t _iv[iv_size];
+ gnutls_datum_t iv = {
+ .data = _iv,
+ .size = iv_size,
+ };
+ uint32_t i;
+ int rc;
+
+ assert_int_equal(session_key.length, 16);
+ assert_int_equal(seq_num_initial.length, 8);
+ assert_int_equal(confounder_initial.length, 8);
+ assert_int_equal(confounder_expected.length, 8);
+ assert_int_equal(clear_initial.length, crypt_expected.length);
+
+ DEBUG(0,("checking buffer size: %d\n", (int)clear_initial.length));
+
+ io = data_blob_dup_talloc(NULL, clear_initial);
+ assert_non_null(io.data);
+ assert_int_equal(io.length, clear_initial.length);
+
+ memcpy(confounder, confounder_initial.data, 8);
+
+ DEBUG(0,("confounder before crypt:\n"));
+ dump_data(0, confounder, 8);
+ DEBUG(0,("initial seq num:\n"));
+ dump_data(0, seq_num_initial.data, 8);
+ DEBUG(0,("io data before crypt:\n"));
+ dump_data(0, io.data, io.length);
+
+ for (i = 0; i < key.size; i++) {
+ key.data[i] = session_key.data[i] ^ 0xf0;
+ }
+
+ ZERO_ARRAY(_iv);
+
+ memcpy(iv.data + 0, seq_num_initial.data, 8);
+ memcpy(iv.data + 8, seq_num_initial.data, 8);
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_AES_128_CFB8,
+ &key,
+ &iv);
+ assert_int_equal(rc, 0);
+
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ confounder,
+ 8);
+ assert_int_equal(rc, 0);
+
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ io.data,
+ io.length);
+ assert_int_equal(rc, 0);
+
+ DEBUG(0,("confounder after crypt:\n"));
+ dump_data(0, confounder, 8);
+ DEBUG(0,("initial seq num:\n"));
+ dump_data(0, seq_num_initial.data, 8);
+ DEBUG(0,("io data after crypt:\n"));
+ dump_data(0, io.data, io.length);
+ assert_memory_equal(io.data, crypt_expected.data, crypt_expected.length);
+ assert_memory_equal(confounder, confounder_expected.data, confounder_expected.length);
+
+ rc = gnutls_cipher_decrypt(cipher_hnd,
+ confounder,
+ 8);
+ assert_int_equal(rc, 0);
+
+ rc = gnutls_cipher_decrypt(cipher_hnd,
+ io.data,
+ io.length);
+ assert_int_equal(rc, 0);
+ gnutls_cipher_deinit(cipher_hnd);
+
+ DEBUG(0,("confounder after decrypt:\n"));
+ dump_data(0, confounder, 8);
+ DEBUG(0,("initial seq num:\n"));
+ dump_data(0, seq_num_initial.data, 8);
+ DEBUG(0,("io data after decrypt:\n"));
+ dump_data(0, io.data, io.length);
+ assert_memory_equal(io.data, clear_initial.data, clear_initial.length);
+ assert_memory_equal(confounder, confounder_initial.data, confounder_initial.length);
+}
+#endif
+
+static void torture_gnutls_aes_128_cfb(void **state)
+{
+#if defined(HAVE_GNUTLS_AES_CFB8) && GNUTLS_VERSION_NUMBER > 0x03060a
+ const uint8_t _session_key[16] = {
+ 0x8E, 0xE8, 0x27, 0x85, 0x83, 0x41, 0x3C, 0x8D,
+ 0xC9, 0x54, 0x70, 0x75, 0x8E, 0xC9, 0x69, 0x91
+ };
+ const DATA_BLOB session_key = data_blob_const(_session_key, 16);
+ const uint8_t _seq_num_initial[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00
+ };
+ const DATA_BLOB seq_num_initial =
+ data_blob_const(_seq_num_initial, 8);
+ const uint8_t _confounder_initial[8] = {
+ 0x6E, 0x09, 0x25, 0x94, 0x01, 0xA0, 0x09, 0x31
+ };
+ const DATA_BLOB confounder_initial =
+ data_blob_const(_confounder_initial, 8);
+ const uint8_t _confounder_expected[8] = {
+ 0xCA, 0xFB, 0xAC, 0xFB, 0xA8, 0x26, 0x75, 0x2A
+ };
+ const DATA_BLOB confounder_expected =
+ data_blob_const(_confounder_expected, 8);
+ const uint8_t _clear_initial[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x8A, 0xE3, 0x13, 0x71, 0x02, 0xF4, 0x36, 0x71,
+ 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x40, 0x28, 0x00, 0x78, 0x57, 0x34, 0x12,
+ 0x34, 0x12, 0xCD, 0xAB, 0xEF, 0x00, 0x01, 0x23,
+ 0x45, 0x67, 0x89, 0xAB, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5D, 0x88, 0x8A, 0xEB, 0x1C, 0xC9, 0x11,
+ 0x9F, 0xE8, 0x08, 0x00, 0x2B, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ const DATA_BLOB clear_initial = data_blob_const(_clear_initial,
+ sizeof(_clear_initial));
+ const uint8_t crypt_buffer[] = {
+ 0xE2, 0xE5, 0xE3, 0x26, 0x45, 0xFB, 0xFC, 0xF3,
+ 0x9C, 0x14, 0xDD, 0xE1, 0x39, 0x23, 0xE0, 0x55,
+ 0xED, 0x8F, 0xF4, 0x92, 0xA1, 0xBD, 0xDC, 0x40,
+ 0x58, 0x6F, 0xD2, 0x5B, 0xF9, 0xC9, 0xA3, 0x87,
+ 0x46, 0x4B, 0x7F, 0xB2, 0x03, 0xD2, 0x35, 0x22,
+ 0x3E, 0x70, 0x9F, 0x1E, 0x3F, 0x1F, 0xDB, 0x7D,
+ 0x79, 0x88, 0x5A, 0x3D, 0xD3, 0x40, 0x1E, 0x69,
+ 0xD7, 0xE2, 0x1D, 0x5A, 0xE9, 0x3B, 0xE1, 0xE2,
+ 0x98, 0xFD, 0xCB, 0x3A, 0xF7, 0xB5, 0x1C, 0xF8,
+ 0xCA, 0x02, 0x00, 0x99, 0x9F, 0x0C, 0x01, 0xE6,
+ 0xD2, 0x00, 0xAF, 0xE0, 0x51, 0x88, 0x62, 0x50,
+ 0xB7, 0xE8, 0x6D, 0x63, 0x4B, 0x97, 0x05, 0xC1,
+ 0xD4, 0x83, 0x96, 0x29, 0x80, 0xAE, 0xD8, 0xA2,
+ 0xED, 0xC9, 0x5D, 0x0D, 0x29, 0xFF, 0x2C, 0x23,
+ 0x02, 0xFA, 0x3B, 0xEE, 0xE8, 0xBA, 0x06, 0x01,
+ 0x95, 0xDF, 0x80, 0x76, 0x0B, 0x17, 0x0E, 0xD8
+ };
+ const DATA_BLOB crypt_expected = data_blob_const(crypt_buffer,
+ sizeof(crypt_buffer));
+ int buffer_sizes[] = {
+ 0, 1, 3, 7, 8, 9, 15, 16, 17
+ };
+ int i;
+
+ torture_gnutls_aes_128_cfb_flags(state,
+ session_key,
+ seq_num_initial,
+ confounder_initial,
+ confounder_expected,
+ clear_initial,
+ crypt_expected);
+
+ /* repeat the test for varying buffer sizes */
+
+ for (i = 0; i < ARRAY_SIZE(buffer_sizes); i++) {
+ DATA_BLOB clear_initial_trunc =
+ data_blob_const(clear_initial.data, buffer_sizes[i]);
+ DATA_BLOB crypt_expected_trunc =
+ data_blob_const(crypt_expected.data, buffer_sizes[i]);
+ torture_gnutls_aes_128_cfb_flags(state,
+ session_key,
+ seq_num_initial,
+ confounder_initial,
+ confounder_expected,
+ clear_initial_trunc,
+ crypt_expected_trunc);
+ }
+#endif
+}
+
+static void torture_gnutls_des_crypt56(void **state)
+{
+ static const uint8_t key[7] = {
+ 0x69, 0x88, 0x96, 0x8E, 0xB5, 0x3A, 0x24
+ };
+ static const uint8_t clear[8] = {
+ 0x3F, 0x49, 0x5B, 0x20, 0xA7, 0x84, 0xC2, 0x34
+ };
+ static const uint8_t crypt_expected[8] = {
+ 0x54, 0x86, 0xCF, 0x51, 0x49, 0x3A, 0x53, 0x5B
+ };
+
+ uint8_t crypt[8];
+ uint8_t decrypt[8];
+ int rc;
+
+ rc = des_crypt56_gnutls(crypt, clear, key, SAMBA_GNUTLS_ENCRYPT);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(crypt, crypt_expected, 8);
+
+ rc = des_crypt56_gnutls(decrypt, crypt, key, SAMBA_GNUTLS_DECRYPT);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(decrypt, clear, 8);
+}
+
+static void torture_gnutls_E_P16(void **state)
+{
+ static const uint8_t key[14] = {
+ 0x98, 0xFD, 0xCB, 0x3A, 0xF7, 0xB5, 0x1C, 0xF8,
+ 0x69, 0x88, 0x96, 0x8E, 0xB5, 0x3A
+ };
+ uint8_t buffer[16] = {
+ 0x9C, 0x14, 0xDD, 0xE1, 0x39, 0x23, 0xE0, 0x55,
+ 0x3F, 0x49, 0x5B, 0x20, 0xA7, 0x84, 0xC2, 0x34
+ };
+ static const uint8_t crypt_expected[16] = {
+ 0x41, 0x4A, 0x7B, 0xEA, 0xAB, 0xBB, 0x95, 0xCE,
+ 0x1D, 0xEA, 0xD9, 0xFF, 0xB0, 0xA9, 0xA4, 0x05
+ };
+
+ int rc;
+
+ rc = E_P16(key, buffer);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(buffer, crypt_expected, 16);
+}
+
+static void torture_gnutls_E_P24(void **state)
+{
+ static const uint8_t key[21] = {
+ 0xFB, 0x67, 0x99, 0xA4, 0x83, 0xF3, 0xD4, 0xED,
+ 0x98, 0xFD, 0xCB, 0x3A, 0xF7, 0xB5, 0x1C, 0xF8,
+ 0x69, 0x88, 0x96, 0x8E, 0x3A
+ };
+ const uint8_t c8[8] = {
+ 0x44, 0xFB, 0xAC, 0xFB, 0x83, 0xB6, 0x75, 0x2A
+ };
+ static const uint8_t crypt_expected[24] = {
+ 0x1A, 0x5E, 0x11, 0xA1, 0x59, 0xA9, 0x6B, 0x4E,
+ 0x12, 0x5D, 0x81, 0x75, 0xA6, 0x62, 0x15, 0x6D,
+ 0x5D, 0x20, 0x25, 0xC1, 0xA3, 0x92, 0xB3, 0x28
+ };
+
+ uint8_t crypt[24];
+ int rc;
+
+ rc = E_P24(key, c8, crypt);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(crypt, crypt_expected, 24);
+}
+
+static void torture_gnutls_SMBOWFencrypt(void **state)
+{
+ static const uint8_t password[16] = {
+ 'M', 'y', 'p', 'a', 's', 's', 'w', 'o',
+ 'r', 'd', 'i', 's', '1', '1', '1', '1'
+ };
+ const uint8_t c8[8] = {
+ 0x79, 0x88, 0x5A, 0x3D, 0xD3, 0x40, 0x1E, 0x69
+ };
+ static const uint8_t crypt_expected[24] = {
+ 0x3F, 0xE3, 0x53, 0x75, 0x81, 0xB4, 0xF0, 0xE7,
+ 0x0C, 0xDE, 0xCD, 0xAE, 0x39, 0x1F, 0x14, 0xB4,
+ 0xA4, 0x2B, 0x3E, 0x39, 0x16, 0xFD, 0x1D, 0x62
+ };
+
+ uint8_t crypt[24];
+ int rc;
+
+ rc = SMBOWFencrypt(password, c8, crypt);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(crypt, crypt_expected, 24);
+}
+
+static void torture_gnutls_E_old_pw_hash(void **state)
+{
+ static uint8_t key[14] = {
+ 0x98, 0xFD, 0xCB, 0x3A, 0xF7, 0xB5, 0x1C, 0xF8,
+ 0x69, 0x88, 0x96, 0x8E, 0xB5, 0x3A
+ };
+ uint8_t clear[16] = {
+ 0x9C, 0x14, 0xDD, 0xE1, 0x39, 0x23, 0xE0, 0x55,
+ 0x3F, 0x49, 0x5B, 0x20, 0xA7, 0x84, 0xC2, 0x34
+ };
+ static const uint8_t crypt_expected[16] = {
+ 0x6A, 0xC7, 0x08, 0xCA, 0x2A, 0xC1, 0xAA, 0x64,
+ 0x37, 0xEF, 0xBE, 0x58, 0xC2, 0x59, 0x33, 0xEC
+ };
+ uint8_t crypt[16];
+ int rc;
+
+ rc = E_old_pw_hash(key, clear, crypt);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(crypt, crypt_expected, 16);
+}
+
+static void torture_gnutls_des_crypt128(void **state)
+{
+ static uint8_t key[16] = {
+ 0x98, 0xFD, 0xCB, 0x3A, 0xF7, 0xB5, 0x1C, 0xF8,
+ 0xA9, 0x69, 0x88, 0x96, 0x8E, 0xB5, 0x3A, 0x24
+ };
+ static const uint8_t clear[8] = {
+ 0x3F, 0x49, 0x5B, 0x20, 0xA7, 0x84, 0xC2, 0x34
+ };
+ static const uint8_t crypt_expected[8] = {
+ 0x4C, 0xB4, 0x4B, 0xD3, 0xC8, 0xC1, 0xA5, 0x50
+ };
+
+ uint8_t crypt[8];
+ int rc;
+
+ rc = des_crypt128(crypt, clear, key);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(crypt, crypt_expected, 8);
+}
+
+static void torture_gnutls_des_crypt112(void **state)
+{
+ static uint8_t key[14] = {
+ 0x98, 0xFD, 0xCB, 0x3A, 0xF7, 0xB5, 0x1C, 0xF8,
+ 0x88, 0x96, 0x8E, 0xB5, 0x3A, 0x24
+ };
+ static const uint8_t clear[8] = {
+ 0x2F, 0x49, 0x5B, 0x20, 0xD7, 0x84, 0xC2, 0x34
+ };
+ static const uint8_t crypt_expected[8] = {
+ 0x87, 0x35, 0xFA, 0xA4, 0x5D, 0x7A, 0xA5, 0x05
+ };
+
+ uint8_t crypt[8];
+ uint8_t decrypt[8];
+ int rc;
+
+ rc = des_crypt112(crypt, clear, key, SAMBA_GNUTLS_ENCRYPT);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(crypt, crypt_expected, 8);
+
+ rc = des_crypt112(decrypt, crypt, key, SAMBA_GNUTLS_DECRYPT);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(decrypt, clear, 8);
+}
+
+static void torture_gnutls_des_crypt112_16(void **state)
+{
+ static uint8_t key[14] = {
+ 0x1E, 0x38, 0x27, 0x5B, 0x3B, 0xB8, 0x67, 0xEB,
+ 0x88, 0x96, 0x8E, 0xB5, 0x3A, 0x24
+ };
+ static const uint8_t clear[16] = {
+ 0x02, 0xFA, 0x3B, 0xEE, 0xE8, 0xBA, 0x06, 0x01,
+ 0xFB, 0x67, 0x99, 0xA4, 0x83, 0xF3, 0xD4, 0xED
+ };
+ static const uint8_t crypt_expected[16] = {
+ 0x3C, 0x10, 0x37, 0x67, 0x96, 0x95, 0xF7, 0x96,
+ 0xAA, 0x03, 0xB9, 0xEA, 0xD6, 0xB3, 0xC3, 0x2D
+ };
+
+ uint8_t crypt[16];
+ uint8_t decrypt[16];
+ int rc;
+
+ rc = des_crypt112_16(crypt, clear, key, SAMBA_GNUTLS_ENCRYPT);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(crypt, crypt_expected, 16);
+
+ rc = des_crypt112_16(decrypt, crypt, key, SAMBA_GNUTLS_DECRYPT);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(decrypt, clear, 16);
+}
+
+static void torture_gnutls_sam_rid_crypt(void **state)
+{
+ static const uint8_t clear[16] = {
+ 0x02, 0xFA, 0x3B, 0xEE, 0xE8, 0xBA, 0x06, 0x01,
+ 0x3F, 0x49, 0x5B, 0x20, 0xA7, 0x84, 0xC2, 0x34
+ };
+ static const uint8_t crypt_expected[16] = {
+ 0x1E, 0x38, 0x27, 0x5B, 0x3B, 0xB8, 0x67, 0xEB,
+ 0xFB, 0x67, 0x99, 0xA4, 0x83, 0xF3, 0xD4, 0xED
+ };
+
+ uint8_t crypt[16];
+ uint8_t decrypt[16];
+ int rid = 500;
+ int rc;
+
+ rc = sam_rid_crypt(rid, clear, crypt, SAMBA_GNUTLS_ENCRYPT);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(crypt, crypt_expected, 16);
+
+ rc = sam_rid_crypt(rid, crypt, decrypt, SAMBA_GNUTLS_DECRYPT);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(decrypt, clear, 16);
+}
+
+static void torture_gnutls_SMBsesskeygen_lm_sess_key(void **state)
+{
+ static const uint8_t lm_hash[16] = {
+ 0xFB, 0x67, 0x99, 0xA4, 0x83, 0xF3, 0xD4, 0xED,
+ 0x9C, 0x14, 0xDD, 0xE1, 0x39, 0x23, 0xE0, 0x55
+ };
+ static const uint8_t lm_resp[24] = {
+ 0x02, 0xFA, 0x3B, 0xEE, 0xE8, 0xBA, 0x06, 0x01,
+ 0x02, 0xFA, 0x3B, 0xEE, 0xE8, 0xBA, 0x06, 0x01,
+ 0x1E, 0x38, 0x27, 0x5B, 0x3B, 0xB8, 0x67, 0xEB
+ };
+ static const uint8_t crypt_expected[16] = {
+ 0x52, 0x8D, 0xB2, 0xD3, 0x89, 0x83, 0xFB, 0x9C,
+ 0x96, 0x45, 0x15, 0x4B, 0xC3, 0xF5, 0xD5, 0x7F
+ };
+
+ uint8_t crypt_sess_key[16];
+ NTSTATUS status;
+
+ status = SMBsesskeygen_lm_sess_key(lm_hash, lm_resp, crypt_sess_key);
+ assert_true(NT_STATUS_IS_OK(status));
+ assert_memory_equal(crypt_sess_key, crypt_expected, 16);
+}
+
+static void torture_gnutls_sess_crypt_blob(void **state)
+{
+ static uint8_t _key[16] = {
+ 0x1E, 0x38, 0x27, 0x5B, 0x3B, 0xB8, 0x67, 0xEB,
+ 0xFA, 0xEE, 0xE8, 0xBA, 0x06, 0x01, 0x2D, 0x95
+ };
+ DATA_BLOB key = data_blob_const(_key, 16);
+ static const uint8_t _clear[24] = {
+ 0x98, 0xFD, 0xCB, 0x3A, 0xF7, 0xB5, 0x1C, 0xF8,
+ 0x02, 0xFA, 0x3B, 0xEE, 0xE8, 0xBA, 0x06, 0x01,
+ 0x3F, 0x49, 0x5B, 0x20, 0xA7, 0x84, 0xC2, 0x34
+ };
+ DATA_BLOB clear = data_blob_const(_clear, 24);
+ static const uint8_t crypt_expected[24] = {
+ 0x2B, 0xDD, 0x3B, 0xFA, 0x48, 0xC9, 0x63, 0x56,
+ 0xAE, 0x8B, 0x3E, 0xCF, 0xEF, 0xDF, 0x7A, 0x42,
+ 0xB3, 0x00, 0x71, 0x7F, 0x5D, 0x1D, 0xE4, 0x70
+ };
+ DATA_BLOB crypt = data_blob(NULL, 24);
+ DATA_BLOB decrypt = data_blob(NULL, 24);
+ int rc;
+
+ rc = sess_crypt_blob(&crypt, &clear, &key, SAMBA_GNUTLS_ENCRYPT);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(crypt.data, crypt_expected, 24);
+
+ rc = sess_crypt_blob(&decrypt, &crypt, &key, SAMBA_GNUTLS_DECRYPT);
+ assert_int_equal(rc, 0);
+ assert_memory_equal(decrypt.data, clear.data, 24);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(torture_gnutls_aes_128_cfb),
+ cmocka_unit_test(torture_gnutls_des_crypt56),
+ cmocka_unit_test(torture_gnutls_E_P16),
+ cmocka_unit_test(torture_gnutls_E_P24),
+ cmocka_unit_test(torture_gnutls_SMBOWFencrypt),
+ cmocka_unit_test(torture_gnutls_E_old_pw_hash),
+ cmocka_unit_test(torture_gnutls_des_crypt128),
+ cmocka_unit_test(torture_gnutls_des_crypt112),
+ cmocka_unit_test(torture_gnutls_des_crypt112_16),
+ cmocka_unit_test(torture_gnutls_sam_rid_crypt),
+ cmocka_unit_test(torture_gnutls_SMBsesskeygen_lm_sess_key),
+ cmocka_unit_test(torture_gnutls_sess_crypt_blob),
+ };
+
+ if (argc == 2) {
+ cmocka_set_test_filter(argv[1]);
+ }
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ rc = cmocka_run_group_tests(tests, NULL, NULL);
+
+ return rc;
+}
diff --git a/libcli/auth/tests/test_rc4_passwd_buffer.c b/libcli/auth/tests/test_rc4_passwd_buffer.c
new file mode 100644
index 0000000..6d97ac6
--- /dev/null
+++ b/libcli/auth/tests/test_rc4_passwd_buffer.c
@@ -0,0 +1,336 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2018-2019 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "includes.h"
+#include "libcli/auth/libcli_auth.h"
+#include "rpc_client/init_samr.h"
+
+#define PASSWORD "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+
+static const uint8_t encrypted_test_blob[] = {
+ 0x37, 0x8e, 0x1d, 0xd5, 0xd3, 0x9f, 0xca, 0x8e,
+ 0x2f, 0x2d, 0xee, 0xc3, 0xb5, 0x50, 0xcd, 0x4e,
+ 0xc9, 0x08, 0x04, 0x68, 0x32, 0xc3, 0xac, 0x8e,
+ 0x53, 0x69, 0xd6, 0xb7, 0x56, 0xcc, 0xc0, 0xbe,
+ 0x4e, 0x96, 0xa7, 0x74, 0xe9, 0xaa, 0x10, 0x3d,
+ 0xd5, 0x8c, 0xaa, 0x12, 0x56, 0xb6, 0xf1, 0x85,
+ 0x21, 0xfa, 0xe9, 0xa1, 0x76, 0xe6, 0xa5, 0x33,
+ 0x33, 0x2f, 0x47, 0x29, 0xd6, 0xbd, 0xde, 0x64,
+ 0x4d, 0x15, 0x3e, 0x6a, 0x11, 0x9b, 0x52, 0xbf,
+ 0x7e, 0x3a, 0xeb, 0x1c, 0x55, 0xd1, 0xb2, 0xa4,
+ 0x35, 0x03, 0x6c, 0x39, 0x61, 0x28, 0x98, 0xc3,
+ 0x2d, 0xd4, 0x70, 0x69, 0x8b, 0x83, 0xe9, 0x62,
+ 0xbe, 0xd8, 0x72, 0x4e, 0xdf, 0xd4, 0xe9, 0xe3,
+ 0x46, 0x2a, 0xf9, 0x3c, 0x0f, 0x41, 0x62, 0xe1,
+ 0x43, 0xf0, 0x91, 0xbe, 0x72, 0xa0, 0xc9, 0x08,
+ 0x73, 0x20, 0x1f, 0x0d, 0x68, 0x2e, 0x32, 0xa1,
+ 0xb8, 0x9b, 0x08, 0xa1, 0xb4, 0x81, 0x6b, 0xf1,
+ 0xde, 0x0c, 0x28, 0x34, 0xe2, 0x65, 0x62, 0x54,
+ 0xeb, 0xc0, 0x71, 0x14, 0xad, 0x36, 0x43, 0x0e,
+ 0x92, 0x4d, 0x11, 0xe8, 0xdd, 0x2d, 0x5f, 0x05,
+ 0xff, 0x07, 0xda, 0x81, 0x4e, 0x27, 0x42, 0xa8,
+ 0xa9, 0x64, 0x4c, 0x74, 0xc8, 0x05, 0xbb, 0x83,
+ 0x5a, 0xd9, 0x90, 0x3e, 0x0d, 0x9d, 0xe5, 0x2f,
+ 0x08, 0xf9, 0x1b, 0xbd, 0x26, 0xc3, 0x0d, 0xac,
+ 0x43, 0xd5, 0x17, 0xf2, 0x61, 0xf5, 0x74, 0x9b,
+ 0xf3, 0x5b, 0x5f, 0xe1, 0x8a, 0xa6, 0xfd, 0xdf,
+ 0xff, 0xb5, 0x8b, 0xf1, 0x26, 0xf7, 0xe0, 0xa7,
+ 0x4f, 0x5b, 0xb8, 0x6d, 0xeb, 0xf6, 0x52, 0x68,
+ 0x8d, 0xa3, 0xd4, 0x7f, 0x56, 0x43, 0xaa, 0xec,
+ 0x58, 0x47, 0x03, 0xee, 0x9b, 0x59, 0xd9, 0x78,
+ 0x9a, 0xfb, 0x9e, 0xe9, 0xa6, 0x61, 0x4e, 0x6d,
+ 0x92, 0x35, 0xd3, 0x37, 0x6e, 0xf2, 0x34, 0x39,
+ 0xd4, 0xd2, 0xeb, 0xcf, 0x1c, 0x10, 0xb3, 0x2b,
+ 0x3e, 0x07, 0x42, 0x3e, 0x20, 0x90, 0x07, 0x3e,
+ 0xc7, 0xed, 0xd4, 0xdf, 0x50, 0xe5, 0xff, 0xaf,
+ 0x05, 0xce, 0x29, 0xbe, 0x01, 0xf8, 0xb0, 0x30,
+ 0x96, 0xae, 0x1b, 0x62, 0x23, 0x93, 0x91, 0x1a,
+ 0x52, 0x98, 0xd9, 0x59, 0xb8, 0x11, 0xec, 0xb8,
+ 0xcf, 0x20, 0x32, 0x90, 0x9e, 0xf2, 0x06, 0x43,
+ 0xb8, 0x36, 0xe3, 0x33, 0x4e, 0x6f, 0x75, 0xeb,
+ 0xf7, 0x6c, 0xac, 0x06, 0x5f, 0x24, 0x8e, 0x4a,
+ 0x03, 0xdf, 0x50, 0x31, 0xaa, 0x91, 0xd5, 0x85,
+ 0x95, 0x78, 0x5b, 0xf4, 0x7f, 0x3e, 0xbc, 0x41,
+ 0xfa, 0x10, 0xd3, 0x0f, 0x86, 0x8b, 0x23, 0xed,
+ 0xfc, 0xcc, 0x3e, 0x7d, 0x8c, 0xb4, 0x7c, 0xec,
+ 0x04, 0x7d, 0x12, 0x53, 0xa1, 0x30, 0xc5, 0xac,
+ 0xf3, 0x1e, 0x54, 0x1f, 0x97, 0x05, 0x86, 0x74,
+ 0x51, 0x13, 0x45, 0x98, 0xb8, 0x50, 0x62, 0x18,
+ 0x0f, 0x6d, 0x66, 0xa4, 0x88, 0x31, 0x76, 0xa3,
+ 0xb0, 0x75, 0x55, 0x44, 0x18, 0x7c, 0x67, 0xc7,
+ 0x09, 0x9c, 0xab, 0x53, 0x49, 0xc0, 0xc9, 0x27,
+ 0x53, 0xa6, 0x99, 0x01, 0x10, 0x49, 0x67, 0x8e,
+ 0x5b, 0x12, 0x96, 0x40, 0x16, 0x39, 0x11, 0x53,
+ 0x44, 0x8f, 0xa9, 0xbe, 0x84, 0xbe, 0xe0, 0x45,
+ 0xe3, 0xfd, 0x48, 0x46, 0x43, 0x53, 0x13, 0x5f,
+ 0xfa, 0xcf, 0x09, 0x67, 0x90, 0xa3, 0x94, 0xaa,
+ 0x0d, 0x1f, 0xc2, 0xc3, 0xd4, 0x7e, 0xc8, 0x14,
+ 0xbe, 0x84, 0xa5, 0x55, 0xee, 0x49, 0x8e, 0x03,
+ 0x1d, 0xaf, 0xad, 0x65, 0x2f, 0xf0, 0xd5, 0x90,
+ 0x5e, 0x8d, 0x29, 0x40, 0xba, 0x57, 0x26, 0xa8,
+ 0x6c, 0x4b, 0x59, 0x40, 0x4e, 0xc2, 0xc4, 0x88,
+ 0x0a, 0x06, 0x2b, 0x6c, 0x2a, 0xc7, 0x3f, 0xfe,
+ 0x37, 0x2c, 0x41, 0x58, 0xfe, 0x7e, 0xaf, 0xd1,
+ 0xd9, 0xd2, 0x9c, 0xd7, 0x8a, 0x01, 0x0e, 0x8c,
+ 0x9e, 0x8b, 0x5d, 0x72, 0x54, 0x00, 0xbe, 0xb2,
+ 0x9a, 0xc7, 0xfd, 0x83, 0x1e, 0x9c, 0x79, 0xdd,
+ 0x15, 0x13, 0xdc, 0x15,
+};
+
+
+static const uint8_t encrypted_wkssvc_test_blob[] = {
+ 0x13, 0x79, 0x1f, 0x1a, 0x02, 0x15, 0x72, 0x1c,
+ 0xa6, 0x26, 0x37, 0xeb, 0x1d, 0x41, 0x7f, 0x76,
+ 0x11, 0x3f, 0x49, 0x4c, 0xf9, 0x69, 0x17, 0xc8,
+ 0x90, 0x92, 0x53, 0xb9, 0x3f, 0xcd, 0x06, 0xfe,
+ 0x5c, 0x17, 0x82, 0xbd, 0x86, 0xab, 0x49, 0xee,
+ 0x61, 0x76, 0x55, 0xc0, 0x10, 0x51, 0xcd, 0xd9,
+ 0x6f, 0x12, 0x86, 0xc6, 0x19, 0x59, 0x9a, 0x2f,
+ 0x27, 0x1d, 0x99, 0x30, 0x60, 0x0d, 0x65, 0xc6,
+ 0x43, 0xd6, 0xda, 0x6b, 0x66, 0x95, 0xd4, 0xca,
+ 0xf5, 0x04, 0xf7, 0x01, 0x5a, 0x55, 0xb0, 0x5e,
+ 0x72, 0x8a, 0x75, 0xe5, 0x33, 0x4c, 0xd8, 0xc4,
+ 0x0e, 0xf4, 0x6d, 0x23, 0xdd, 0x05, 0x90, 0xff,
+ 0xe0, 0x91, 0x7b, 0x62, 0x86, 0xee, 0x78, 0x31,
+ 0x07, 0xad, 0x8b, 0xf9, 0xdf, 0x6f, 0x8b, 0xbd,
+ 0x15, 0xde, 0x1b, 0xae, 0x84, 0x68, 0xe5, 0x41,
+ 0x7a, 0xe3, 0x47, 0x99, 0xba, 0x61, 0xe5, 0x51,
+ 0x64, 0x9a, 0xa0, 0x41, 0x44, 0xa1, 0x3a, 0x52,
+ 0x59, 0x7d, 0x6c, 0xcf, 0xcc, 0xf0, 0x11, 0xbc,
+ 0xb7, 0x51, 0xa9, 0xd8, 0xfd, 0xbf, 0x58, 0x77,
+ 0x28, 0x86, 0xa1, 0x27, 0x94, 0xe5, 0xf6, 0x1a,
+ 0x6b, 0x76, 0xf7, 0x72, 0x6e, 0x17, 0x09, 0xd8,
+ 0x3c, 0x6f, 0x39, 0x91, 0xea, 0x48, 0x98, 0xdc,
+ 0x1d, 0x50, 0x2e, 0x02, 0x6e, 0x7f, 0x80, 0x5d,
+ 0x6e, 0x96, 0xe1, 0xcf, 0x8b, 0x6b, 0xb6, 0xed,
+ 0xb4, 0x6a, 0x08, 0xd2, 0x45, 0x09, 0x88, 0x86,
+ 0x32, 0x58, 0xd8, 0x5e, 0x33, 0x8c, 0x29, 0x1a,
+ 0x8f, 0xc5, 0x54, 0x9b, 0xa8, 0x32, 0xb2, 0xc1,
+ 0x72, 0x14, 0x6c, 0x5d, 0x9d, 0xd3, 0xf2, 0x6c,
+ 0x6e, 0xa4, 0x84, 0x52, 0x26, 0x73, 0x7a, 0x30,
+ 0x56, 0x75, 0xef, 0xd1, 0x9d, 0xcd, 0xb7, 0x87,
+ 0xa9, 0x5c, 0xaf, 0xe6, 0xda, 0x1d, 0x3c, 0x9c,
+ 0xa3, 0xb1, 0x03, 0xb0, 0x8e, 0xf6, 0xc8, 0x8f,
+ 0x57, 0x1c, 0xce, 0x05, 0x54, 0x99, 0xf1, 0xf9,
+ 0x35, 0xe6, 0xf7, 0x67, 0x94, 0xb2, 0x67, 0x5b,
+ 0xe7, 0xa0, 0xa2, 0x1e, 0xa2, 0x74, 0xd3, 0x99,
+ 0x9c, 0xd5, 0xd9, 0x90, 0x86, 0x24, 0x0e, 0x1a,
+ 0x0d, 0xc8, 0x9e, 0x68, 0x4c, 0x43, 0x2f, 0x42,
+ 0xb1, 0x7c, 0xce, 0x1e, 0xb6, 0xac, 0x56, 0xb0,
+ 0x8d, 0x93, 0xf1, 0x53, 0x7d, 0x0e, 0x00, 0x46,
+ 0xba, 0x2e, 0x14, 0x7a, 0x0b, 0xaa, 0xcb, 0x07,
+ 0x9b, 0x09, 0x05, 0xa0, 0xd3, 0xa1, 0x80, 0xc2,
+ 0xd3, 0x59, 0x92, 0x27, 0x66, 0x1f, 0xdd, 0x76,
+ 0x36, 0xb3, 0x3c, 0xeb, 0xd7, 0x61, 0x94, 0xb1,
+ 0xf8, 0x3a, 0xe0, 0xba, 0x91, 0x0f, 0xef, 0x72,
+ 0x2b, 0x26, 0xc6, 0xb8, 0x6d, 0x0b, 0xdb, 0x60,
+ 0xf8, 0xb4, 0x98, 0xd7, 0x8b, 0x8d, 0xfb, 0xa7,
+ 0x4e, 0x27, 0xeb, 0x00, 0xe8, 0xf7, 0x5a, 0xec,
+ 0xf5, 0x60, 0x28, 0x37, 0xb2, 0xc4, 0x13, 0x48,
+ 0x2a, 0xe1, 0x34, 0xb2, 0x53, 0xcb, 0x44, 0x34,
+ 0x08, 0x7e, 0x56, 0x5c, 0x2b, 0x9b, 0xe2, 0xca,
+ 0x90, 0xb0, 0x57, 0xee, 0x10, 0x88, 0x39, 0x84,
+ 0xc6, 0x66, 0x07, 0x50, 0x63, 0xcc, 0x2a, 0x7c,
+ 0x99, 0x8c, 0x05, 0xf9, 0xf0, 0xb8, 0x62, 0xf0,
+ 0x92, 0xf7, 0x2a, 0x4a, 0x17, 0x14, 0x78, 0xa1,
+ 0x71, 0xb6, 0x42, 0xf0, 0x87, 0xa8, 0xa4, 0x48,
+ 0xeb, 0xdb, 0xed, 0x8a, 0x15, 0x19, 0x1a, 0xd9,
+ 0xfe, 0x6f, 0x07, 0x93, 0x5d, 0x39, 0xe8, 0x0e,
+ 0x47, 0xe6, 0x7a, 0x7d, 0x52, 0x2e, 0x40, 0x6f,
+ 0x31, 0x1b, 0xf6, 0x0c, 0xc2, 0x83, 0xae, 0xc1,
+ 0xf0, 0xf5, 0x71, 0xcd, 0xe2, 0xf5, 0x19, 0xb6,
+ 0xd8, 0xb0, 0x4d, 0xa9, 0x51, 0x1c, 0xb4, 0xaf,
+ 0x69, 0x9a, 0x89, 0xb6, 0x5b, 0x4d, 0xfa, 0x1b,
+ 0xca, 0xc8, 0x61, 0x92, 0x3a, 0xd6, 0x76, 0xad,
+ 0x5d, 0xa6, 0x17, 0x60, 0x3e, 0xea, 0x94, 0xcf,
+ 0x6d, 0x1b, 0x98, 0x5c, 0x19, 0x9e, 0x4e, 0xd3,
+ 0x21, 0x55, 0xda, 0xe3,
+};
+
+static void torture_decode_rc4_passwd_buffer(void **state)
+{
+ char *password_decoded = NULL;
+ size_t password_decoded_len = 0;
+ DATA_BLOB session_key = data_blob_const("SystemLibraryDTC", 16);
+ struct samr_CryptPasswordEx out_pwd_buf = {
+ .data = {0},
+ };
+ NTSTATUS status;
+ bool ok;
+
+ memcpy(out_pwd_buf.data,
+ encrypted_test_blob,
+ sizeof(out_pwd_buf.data));
+
+ status = decode_rc4_passwd_buffer(&session_key, &out_pwd_buf);
+ assert_true(NT_STATUS_IS_OK(status));
+
+ ok = decode_pw_buffer(NULL,
+ out_pwd_buf.data,
+ &password_decoded,
+ &password_decoded_len,
+ CH_UTF16);
+ assert_true(ok);
+ assert_int_equal(password_decoded_len, strlen(PASSWORD));
+ assert_string_equal(password_decoded, PASSWORD);
+}
+
+static void torture_rc4_passwd_buffer(void **state)
+{
+ char *password_decoded = NULL;
+ size_t password_decoded_len = 0;
+ DATA_BLOB session_key = data_blob_const("SystemLibraryDTC", 16);
+ struct samr_CryptPasswordEx out_pwd_buf = {
+ .data = {0},
+ };
+ NTSTATUS status;
+ bool ok;
+
+ status = init_samr_CryptPasswordEx(PASSWORD,
+ &session_key,
+ &out_pwd_buf);
+ assert_true(NT_STATUS_IS_OK(status));
+
+ status = decode_rc4_passwd_buffer(&session_key, &out_pwd_buf);
+ assert_true(NT_STATUS_IS_OK(status));
+
+ ok = decode_pw_buffer(NULL,
+ out_pwd_buf.data,
+ &password_decoded,
+ &password_decoded_len,
+ CH_UTF16);
+ assert_true(ok);
+ assert_int_equal(password_decoded_len, strlen(PASSWORD));
+ assert_string_equal(password_decoded, PASSWORD);
+ talloc_free(password_decoded);
+}
+
+static void torture_endode_decode_rc4_passwd_buffer(void **state)
+{
+ char *password_decoded = NULL;
+ size_t password_decoded_len = 0;
+ DATA_BLOB session_key = data_blob_const("SystemLibraryDTC", 16);
+ struct samr_CryptPasswordEx out_pwd_buf = {
+ .data = {0},
+ };
+ NTSTATUS status;
+ bool ok;
+
+ status = encode_rc4_passwd_buffer(PASSWORD,
+ &session_key,
+ &out_pwd_buf);
+ assert_true(NT_STATUS_IS_OK(status));
+
+ status = decode_rc4_passwd_buffer(&session_key, &out_pwd_buf);
+ assert_true(NT_STATUS_IS_OK(status));
+
+ ok = decode_pw_buffer(NULL,
+ out_pwd_buf.data,
+ &password_decoded,
+ &password_decoded_len,
+ CH_UTF16);
+ assert_true(ok);
+ assert_int_equal(password_decoded_len, strlen(PASSWORD));
+ assert_string_equal(password_decoded, PASSWORD);
+ talloc_free(password_decoded);
+}
+
+static void torture_decode_wkssvc_join_password_buffer(void **state)
+{
+ DATA_BLOB session_key = data_blob_const("SystemLibraryDTC", 16);
+ struct wkssvc_PasswordBuffer pwd_buf = {
+ .data = {0},
+ };
+ char *password_decoded = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ WERROR werr;
+
+ mem_ctx = talloc_new(NULL);
+ assert_non_null(mem_ctx);
+
+ memcpy(pwd_buf.data,
+ encrypted_wkssvc_test_blob,
+ sizeof(pwd_buf.data));
+
+ werr = decode_wkssvc_join_password_buffer(mem_ctx,
+ &pwd_buf,
+ &session_key,
+ &password_decoded);
+ assert_true(W_ERROR_IS_OK(werr));
+ assert_non_null(password_decoded);
+ assert_string_equal(password_decoded, PASSWORD);
+
+ TALLOC_FREE(mem_ctx);
+}
+
+static void torture_wkssvc_join_password_buffer(void **state)
+{
+ DATA_BLOB session_key = data_blob_const("SystemLibraryDTC", 16);
+ struct wkssvc_PasswordBuffer *pwd_buf = NULL;
+ char *password_decoded = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ WERROR werr;
+
+ mem_ctx = talloc_new(NULL);
+ assert_non_null(mem_ctx);
+
+ werr = encode_wkssvc_join_password_buffer(mem_ctx,
+ PASSWORD,
+ &session_key,
+ &pwd_buf);
+ assert_true(W_ERROR_IS_OK(werr));
+ assert_non_null(pwd_buf);
+
+ werr = decode_wkssvc_join_password_buffer(mem_ctx,
+ pwd_buf,
+ &session_key,
+ &password_decoded);
+ assert_true(W_ERROR_IS_OK(werr));
+ assert_non_null(password_decoded);
+ assert_string_equal(password_decoded, PASSWORD);
+
+ TALLOC_FREE(mem_ctx);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(torture_decode_rc4_passwd_buffer),
+ cmocka_unit_test(torture_rc4_passwd_buffer),
+ cmocka_unit_test(torture_endode_decode_rc4_passwd_buffer),
+ cmocka_unit_test(torture_decode_wkssvc_join_password_buffer),
+ cmocka_unit_test(torture_wkssvc_join_password_buffer),
+ };
+
+ if (argc == 2) {
+ cmocka_set_test_filter(argv[1]);
+ }
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ rc = cmocka_run_group_tests(tests, NULL, NULL);
+
+ return rc;
+}
diff --git a/libcli/auth/tests/test_schannel.c b/libcli/auth/tests/test_schannel.c
new file mode 100644
index 0000000..b1c88fd
--- /dev/null
+++ b/libcli/auth/tests/test_schannel.c
@@ -0,0 +1,305 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2019 Guenther Deschner <gd@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "includes.h"
+#include "auth/gensec/schannel.c"
+
+static void torture_schannel_seal_flags(void **state, uint32_t flags,
+ const DATA_BLOB session_key,
+ const DATA_BLOB seq_num_initial,
+ const DATA_BLOB confounder_initial,
+ const DATA_BLOB confounder_expected,
+ const DATA_BLOB clear_initial,
+ const DATA_BLOB crypt_expected)
+{
+ NTSTATUS status;
+ struct schannel_state *schannel_state;
+ struct netlogon_creds_CredentialState *creds;
+ uint8_t confounder[8];
+ DATA_BLOB io;
+
+ assert_int_equal(session_key.length, 16);
+ assert_int_equal(seq_num_initial.length, 8);
+ assert_int_equal(confounder_initial.length, 8);
+ assert_int_equal(confounder_expected.length, 8);
+ assert_int_equal(clear_initial.length, crypt_expected.length);
+
+ DEBUG(0,("checking buffer size: %d\n", (int)clear_initial.length));
+
+ schannel_state = talloc_zero(NULL, struct schannel_state);
+ assert_non_null(schannel_state);
+ creds = talloc_zero(schannel_state,
+ struct netlogon_creds_CredentialState);
+ assert_non_null(creds);
+ schannel_state->creds = creds;
+
+ io = data_blob_dup_talloc(schannel_state, clear_initial);
+ assert_non_null(io.data);
+ assert_int_equal(io.length, clear_initial.length);
+
+ schannel_state->creds->negotiate_flags = flags;
+ memcpy(schannel_state->creds->session_key, session_key.data, 16);
+
+ memcpy(confounder, confounder_initial.data, 8);
+
+ DEBUG(0,("confounder before crypt:\n"));
+ dump_data(0, confounder, 8);
+ dump_data(0, seq_num_initial.data, 8);
+ dump_data(0, io.data, io.length);
+
+ status = netsec_do_seal(schannel_state,
+ seq_num_initial.data,
+ confounder,
+ io.data,
+ io.length,
+ true);
+
+ assert_true(NT_STATUS_IS_OK(status));
+ dump_data(0, io.data, io.length);
+ DEBUG(0,("confounder after crypt:\n"));
+ dump_data(0, confounder, 8);
+ dump_data(0, seq_num_initial.data, 8);
+ assert_memory_equal(io.data, crypt_expected.data, crypt_expected.length);
+ assert_memory_equal(confounder, confounder_expected.data, confounder_expected.length);
+
+ status = netsec_do_seal(schannel_state,
+ seq_num_initial.data,
+ confounder,
+ io.data,
+ io.length,
+ false);
+
+ assert_true(NT_STATUS_IS_OK(status));
+ dump_data(0, io.data, io.length);
+ DEBUG(0,("confounder after decrypt:\n"));
+ dump_data(0, confounder, 8);
+ dump_data(0, seq_num_initial.data, 8);
+ assert_memory_equal(io.data, clear_initial.data, clear_initial.length);
+ assert_memory_equal(confounder, confounder_initial.data, confounder_initial.length);
+
+ talloc_free(schannel_state);
+}
+
+static void torture_schannel_seal_rc4(void **state)
+{
+ const uint8_t _session_key[16] = {
+ 0x14, 0xD5, 0x7F, 0x8D, 0x8E, 0xCF, 0xFB, 0x56,
+ 0x71, 0x29, 0x9D, 0x9C, 0x2A, 0x75, 0x00, 0xA1
+ };
+ const DATA_BLOB session_key = data_blob_const(_session_key, 16);
+ const uint8_t _seq_num_initial[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00
+ };
+ const DATA_BLOB seq_num_initial =
+ data_blob_const(_seq_num_initial, 8);
+ const uint8_t _confounder_initial[8] = {
+ 0x1A, 0x5A, 0xE8, 0xC7, 0xBE, 0x4F, 0x1F, 0x07
+ };
+ const DATA_BLOB confounder_initial =
+ data_blob_const(_confounder_initial, 8);
+ const uint8_t _confounder_expected[8] = {
+ 0x25, 0x4A, 0x9C, 0x15, 0x82, 0x3E, 0x4A, 0x42
+ };
+ const DATA_BLOB confounder_expected =
+ data_blob_const(_confounder_expected, 8);
+ const uint8_t _clear_initial[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x8A, 0xE3, 0x13, 0x71, 0x02, 0xF4, 0x36, 0x71,
+ 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x40, 0x28, 0x00, 0x78, 0x57, 0x34, 0x12,
+ 0x34, 0x12, 0xCD, 0xAB, 0xEF, 0x00, 0x01, 0x23,
+ 0x45, 0x67, 0x89, 0xAB, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5D, 0x88, 0x8A, 0xEB, 0x1C, 0xC9, 0x11,
+ 0x9F, 0xE8, 0x08, 0x00, 0x2B, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ const DATA_BLOB clear_initial = data_blob_const(_clear_initial,
+ sizeof(_clear_initial));
+ const uint8_t crypt_buffer[] = {
+ 0x3E, 0x10, 0x74, 0xD2, 0x3C, 0x71, 0x57, 0x45,
+ 0xB8, 0xAA, 0xCF, 0xE3, 0x84, 0xBE, 0xC4, 0x00,
+ 0xF4, 0x4D, 0x88, 0x0A, 0x9B, 0xCC, 0x53, 0xFC,
+ 0x32, 0xAA, 0x8E, 0x4B, 0x0E, 0xDE, 0x5F, 0x7D,
+ 0x6D, 0x31, 0x4E, 0xAB, 0xE0, 0x7D, 0x37, 0x9D,
+ 0x3D, 0x16, 0xD8, 0xBA, 0x6A, 0xB0, 0xD0, 0x99,
+ 0x14, 0x05, 0x37, 0xCF, 0x63, 0xD3, 0xD7, 0x60,
+ 0x63, 0x3C, 0x03, 0x0A, 0x30, 0xA0, 0x3E, 0xC7,
+ 0xDA, 0x94, 0x3B, 0x40, 0x63, 0x74, 0xEF, 0xCF,
+ 0xE5, 0x48, 0x87, 0xE9, 0x6A, 0x5A, 0xC7, 0x61,
+ 0xF7, 0x09, 0xB7, 0x7C, 0xDE, 0xDB, 0xB0, 0x94,
+ 0x9B, 0x99, 0xC0, 0xA7, 0x7E, 0x78, 0x09, 0x35,
+ 0xB4, 0xF4, 0x11, 0xC3, 0xB3, 0x77, 0xB5, 0x77,
+ 0x25, 0xEE, 0xFD, 0x2F, 0x9A, 0x15, 0x95, 0x27,
+ 0x08, 0xDA, 0xD0, 0x28, 0xD6, 0x31, 0xB4, 0xB7,
+ 0x7A, 0x19, 0xBB, 0xF3, 0x78, 0xF8, 0xC2, 0x5B
+ };
+ const DATA_BLOB crypt_expected = data_blob_const(crypt_buffer,
+ sizeof(crypt_buffer));
+ int buffer_sizes[] = {
+ 0, 1, 3, 7, 8, 9, 15, 16, 17
+ };
+ int i;
+
+ torture_schannel_seal_flags(state, 0,
+ session_key,
+ seq_num_initial,
+ confounder_initial,
+ confounder_expected,
+ clear_initial,
+ crypt_expected);
+
+ /* repeat the test for varying buffer sizes */
+
+ for (i = 0; i < ARRAY_SIZE(buffer_sizes); i++) {
+ DATA_BLOB clear_initial_trunc =
+ data_blob_const(clear_initial.data, buffer_sizes[i]);
+ DATA_BLOB crypt_expected_trunc =
+ data_blob_const(crypt_expected.data, buffer_sizes[i]);
+ torture_schannel_seal_flags(state, 0,
+ session_key,
+ seq_num_initial,
+ confounder_initial,
+ confounder_expected,
+ clear_initial_trunc,
+ crypt_expected_trunc);
+ }
+}
+
+static void torture_schannel_seal_aes(void **state)
+{
+ const uint8_t _session_key[16] = {
+ 0x8E, 0xE8, 0x27, 0x85, 0x83, 0x41, 0x3C, 0x8D,
+ 0xC9, 0x54, 0x70, 0x75, 0x8E, 0xC9, 0x69, 0x91
+ };
+ const DATA_BLOB session_key = data_blob_const(_session_key, 16);
+ const uint8_t _seq_num_initial[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00
+ };
+ const DATA_BLOB seq_num_initial =
+ data_blob_const(_seq_num_initial, 8);
+ const uint8_t _confounder_initial[8] = {
+ 0x6E, 0x09, 0x25, 0x94, 0x01, 0xA0, 0x09, 0x31
+ };
+ const DATA_BLOB confounder_initial =
+ data_blob_const(_confounder_initial, 8);
+ const uint8_t _confounder_expected[8] = {
+ 0xCA, 0xFB, 0xAC, 0xFB, 0xA8, 0x26, 0x75, 0x2A
+ };
+ const DATA_BLOB confounder_expected =
+ data_blob_const(_confounder_expected, 8);
+ const uint8_t _clear_initial[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x8A, 0xE3, 0x13, 0x71, 0x02, 0xF4, 0x36, 0x71,
+ 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x40, 0x28, 0x00, 0x78, 0x57, 0x34, 0x12,
+ 0x34, 0x12, 0xCD, 0xAB, 0xEF, 0x00, 0x01, 0x23,
+ 0x45, 0x67, 0x89, 0xAB, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x5D, 0x88, 0x8A, 0xEB, 0x1C, 0xC9, 0x11,
+ 0x9F, 0xE8, 0x08, 0x00, 0x2B, 0x10, 0x48, 0x60,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ const DATA_BLOB clear_initial = data_blob_const(_clear_initial,
+ sizeof(_clear_initial));
+ const uint8_t crypt_buffer[] = {
+ 0xE2, 0xE5, 0xE3, 0x26, 0x45, 0xFB, 0xFC, 0xF3,
+ 0x9C, 0x14, 0xDD, 0xE1, 0x39, 0x23, 0xE0, 0x55,
+ 0xED, 0x8F, 0xF4, 0x92, 0xA1, 0xBD, 0xDC, 0x40,
+ 0x58, 0x6F, 0xD2, 0x5B, 0xF9, 0xC9, 0xA3, 0x87,
+ 0x46, 0x4B, 0x7F, 0xB2, 0x03, 0xD2, 0x35, 0x22,
+ 0x3E, 0x70, 0x9F, 0x1E, 0x3F, 0x1F, 0xDB, 0x7D,
+ 0x79, 0x88, 0x5A, 0x3D, 0xD3, 0x40, 0x1E, 0x69,
+ 0xD7, 0xE2, 0x1D, 0x5A, 0xE9, 0x3B, 0xE1, 0xE2,
+ 0x98, 0xFD, 0xCB, 0x3A, 0xF7, 0xB5, 0x1C, 0xF8,
+ 0xCA, 0x02, 0x00, 0x99, 0x9F, 0x0C, 0x01, 0xE6,
+ 0xD2, 0x00, 0xAF, 0xE0, 0x51, 0x88, 0x62, 0x50,
+ 0xB7, 0xE8, 0x6D, 0x63, 0x4B, 0x97, 0x05, 0xC1,
+ 0xD4, 0x83, 0x96, 0x29, 0x80, 0xAE, 0xD8, 0xA2,
+ 0xED, 0xC9, 0x5D, 0x0D, 0x29, 0xFF, 0x2C, 0x23,
+ 0x02, 0xFA, 0x3B, 0xEE, 0xE8, 0xBA, 0x06, 0x01,
+ 0x95, 0xDF, 0x80, 0x76, 0x0B, 0x17, 0x0E, 0xD8
+ };
+ const DATA_BLOB crypt_expected = data_blob_const(crypt_buffer,
+ sizeof(crypt_buffer));
+ int buffer_sizes[] = {
+ 0, 1, 3, 7, 8, 9, 15, 16, 17
+ };
+ int i;
+
+ torture_schannel_seal_flags(state, NETLOGON_NEG_SUPPORTS_AES,
+ session_key,
+ seq_num_initial,
+ confounder_initial,
+ confounder_expected,
+ clear_initial,
+ crypt_expected);
+
+ /* repeat the test for varying buffer sizes */
+
+ for (i = 0; i < ARRAY_SIZE(buffer_sizes); i++) {
+ DATA_BLOB clear_initial_trunc =
+ data_blob_const(clear_initial.data, buffer_sizes[i]);
+ DATA_BLOB crypt_expected_trunc =
+ data_blob_const(crypt_expected.data, buffer_sizes[i]);
+ torture_schannel_seal_flags(state, NETLOGON_NEG_SUPPORTS_AES,
+ session_key,
+ seq_num_initial,
+ confounder_initial,
+ confounder_expected,
+ clear_initial_trunc,
+ crypt_expected_trunc);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(torture_schannel_seal_rc4),
+ cmocka_unit_test(torture_schannel_seal_aes),
+ };
+
+ if (argc == 2) {
+ cmocka_set_test_filter(argv[1]);
+ }
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ rc = cmocka_run_group_tests(tests, NULL, NULL);
+
+ return rc;
+}
diff --git a/libcli/auth/wscript_build b/libcli/auth/wscript_build
new file mode 100644
index 0000000..c73e0d1
--- /dev/null
+++ b/libcli/auth/wscript_build
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+
+bld.SAMBA_LIBRARY('cliauth',
+ source='',
+ deps='MSRPC_PARSE LIBCLI_AUTH COMMON_SCHANNEL PAM_ERRORS SPNEGO_PARSE krb5samba samba-errors NTLM_CHECK UTIL_LSARPC',
+ private_library=True,
+ grouping_library=True)
+
+bld.SAMBA_SUBSYSTEM('MSRPC_PARSE',
+ source='msrpc_parse.c',
+ deps='talloc'
+ )
+
+bld.SAMBA_SUBSYSTEM('NTLM_CHECK',
+ source='ntlm_check.c',
+ deps = 'talloc LIBCLI_AUTH'
+ )
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_AUTH',
+ source='credentials.c session.c smbencrypt.c smbdes.c',
+ public_deps='MSRPC_PARSE gnutls GNUTLS_HELPERS util_str_escape',
+ public_headers='credentials.h:domain_credentials.h'
+ )
+
+
+bld.SAMBA_SUBSYSTEM('COMMON_SCHANNEL',
+ source='schannel_state_tdb.c',
+ deps='dbwrap util_tdb samba-hostconfig NDR_NETLOGON'
+ )
+
+bld.SAMBA_SUBSYSTEM('NETLOGON_CREDS_CLI',
+ source='netlogon_creds_cli.c',
+ deps='''
+ dbwrap
+ util_tdb
+ tevent-util
+ samba-hostconfig
+ gensec
+ RPC_NDR_NETLOGON
+ NDR_NETLOGON
+ '''
+ )
+
+bld.SAMBA_SUBSYSTEM('PAM_ERRORS',
+ source='pam_errors.c',
+ deps='talloc'
+ )
+
+bld.SAMBA_SUBSYSTEM('SPNEGO_PARSE',
+ source='spnego_parse.c',
+ deps='asn1util')
+
+bld.SAMBA_BINARY(
+ 'test_ntlm_check',
+ source='tests/ntlm_check.c',
+ deps='''
+ NTLM_CHECK
+ CREDENTIALS_NTLM
+ samba-credentials
+ cmocka
+ talloc
+ ''',
+ for_selftest=True
+ )
+
+bld.SAMBA_BINARY('test_rc4_passwd_buffer',
+ source='tests/test_rc4_passwd_buffer.c',
+ deps='''
+ INIT_SAMR
+ LIBCLI_AUTH
+ cmocka
+ ''',
+ for_selftest=True)
+
+bld.SAMBA_BINARY('test_schannel',
+ source='tests/test_schannel.c',
+ deps='''
+ gensec
+ cmocka
+ ''',
+ for_selftest=True)
+
+bld.SAMBA_BINARY('test_gnutls',
+ source='tests/test_gnutls.c',
+ deps='''
+ gnutls
+ LIBCLI_AUTH
+ cmocka
+ samba-util
+ ''',
+ for_selftest=True)
+
+bld.SAMBA_BINARY('test_encode_decode',
+ source='tests/test_encode_decode.c',
+ deps='''
+ LIBCLI_AUTH
+ cmocka
+ samba-util
+ ''',
+ for_selftest=True)