summaryrefslogtreecommitdiff
path: root/source4/libcli
diff options
context:
space:
mode:
Diffstat (limited to 'source4/libcli')
-rw-r--r--source4/libcli/auth/config.mk17
-rw-r--r--source4/libcli/auth/credentials.c375
-rw-r--r--source4/libcli/auth/credentials.h46
-rw-r--r--source4/libcli/auth/libcli_auth.h24
-rw-r--r--source4/libcli/auth/session.c218
-rw-r--r--source4/libcli/auth/smbdes.c381
-rw-r--r--source4/libcli/auth/smbencrypt.c595
-rw-r--r--source4/libcli/cldap/cldap.c738
-rw-r--r--source4/libcli/cldap/cldap.h182
-rw-r--r--source4/libcli/cliconnect.c267
-rw-r--r--source4/libcli/clideltree.c137
-rw-r--r--source4/libcli/clifile.c702
-rw-r--r--source4/libcli/clilist.c356
-rw-r--r--source4/libcli/climessage.c95
-rw-r--r--source4/libcli/clireadwrite.c167
-rw-r--r--source4/libcli/clitrans2.c224
-rw-r--r--source4/libcli/composite/composite.c218
-rw-r--r--source4/libcli/composite/composite.h109
-rw-r--r--source4/libcli/config.mk169
-rw-r--r--source4/libcli/dgram/browse.c114
-rw-r--r--source4/libcli/dgram/dgramsocket.c245
-rw-r--r--source4/libcli/dgram/libdgram.h157
-rw-r--r--source4/libcli/dgram/mailslot.c226
-rw-r--r--source4/libcli/dgram/netlogon.c145
-rw-r--r--source4/libcli/finddcs.c276
-rw-r--r--source4/libcli/ldap/config.mk12
-rw-r--r--source4/libcli/ldap/ldap.h31
-rw-r--r--source4/libcli/ldap/ldap_bind.c413
-rw-r--r--source4/libcli/ldap/ldap_client.c832
-rw-r--r--source4/libcli/ldap/ldap_client.h140
-rw-r--r--source4/libcli/ldap/ldap_controls.c1237
-rw-r--r--source4/libcli/ldap/ldap_ildap.c129
-rw-r--r--source4/libcli/libcli.h70
-rw-r--r--source4/libcli/rap/rap.h358
-rw-r--r--source4/libcli/raw/README5
-rw-r--r--source4/libcli/raw/clierror.c73
-rw-r--r--source4/libcli/raw/clioplock.c62
-rw-r--r--source4/libcli/raw/clisession.c297
-rw-r--r--source4/libcli/raw/clisocket.c249
-rw-r--r--source4/libcli/raw/clitransport.c680
-rw-r--r--source4/libcli/raw/clitree.c216
-rw-r--r--source4/libcli/raw/interfaces.h2766
-rw-r--r--source4/libcli/raw/ioctl.h59
-rw-r--r--source4/libcli/raw/libcliraw.h384
-rw-r--r--source4/libcli/raw/rawacl.c163
-rw-r--r--source4/libcli/raw/rawdate.c82
-rw-r--r--source4/libcli/raw/raweas.c365
-rw-r--r--source4/libcli/raw/rawfile.c1017
-rw-r--r--source4/libcli/raw/rawfileinfo.c776
-rw-r--r--source4/libcli/raw/rawfsinfo.c336
-rw-r--r--source4/libcli/raw/rawioctl.c173
-rw-r--r--source4/libcli/raw/rawlpq.c49
-rw-r--r--source4/libcli/raw/rawnegotiate.c206
-rw-r--r--source4/libcli/raw/rawnotify.c168
-rw-r--r--source4/libcli/raw/rawreadwrite.c349
-rw-r--r--source4/libcli/raw/rawrequest.c1029
-rw-r--r--source4/libcli/raw/rawsearch.c841
-rw-r--r--source4/libcli/raw/rawsetfileinfo.c492
-rw-r--r--source4/libcli/raw/rawshadow.c82
-rw-r--r--source4/libcli/raw/rawtrans.c961
-rw-r--r--source4/libcli/raw/request.h78
-rw-r--r--source4/libcli/raw/signing.h43
-rw-r--r--source4/libcli/raw/smb.h622
-rw-r--r--source4/libcli/raw/smb_signing.c407
-rw-r--r--source4/libcli/raw/trans2.h476
-rw-r--r--source4/libcli/resolve/bcast.c106
-rw-r--r--source4/libcli/resolve/dns_ex.c541
-rw-r--r--source4/libcli/resolve/host.c60
-rw-r--r--source4/libcli/resolve/nbtlist.c224
-rw-r--r--source4/libcli/resolve/resolve.c283
-rw-r--r--source4/libcli/resolve/resolve.h53
-rw-r--r--source4/libcli/resolve/resolve_lp.c46
-rw-r--r--source4/libcli/resolve/testsuite.c90
-rw-r--r--source4/libcli/resolve/wins.c82
-rw-r--r--source4/libcli/security/access_check.c155
-rw-r--r--source4/libcli/security/config.mk10
-rw-r--r--source4/libcli/security/privilege.c241
-rw-r--r--source4/libcli/security/sddl.c598
-rw-r--r--source4/libcli/security/security.h35
-rw-r--r--source4/libcli/security/security_descriptor.c533
-rw-r--r--source4/libcli/security/security_token.c169
-rw-r--r--source4/libcli/security/tests/bindings.py97
-rw-r--r--source4/libcli/security/tests/sddl.c105
-rw-r--r--source4/libcli/smb2/break.c74
-rw-r--r--source4/libcli/smb2/cancel.c77
-rw-r--r--source4/libcli/smb2/close.c80
-rw-r--r--source4/libcli/smb2/config.mk10
-rw-r--r--source4/libcli/smb2/connect.c311
-rw-r--r--source4/libcli/smb2/create.c419
-rw-r--r--source4/libcli/smb2/find.c180
-rw-r--r--source4/libcli/smb2/flush.c70
-rw-r--r--source4/libcli/smb2/getinfo.c220
-rw-r--r--source4/libcli/smb2/ioctl.c109
-rw-r--r--source4/libcli/smb2/keepalive.c65
-rw-r--r--source4/libcli/smb2/lock.c82
-rw-r--r--source4/libcli/smb2/logoff.c69
-rw-r--r--source4/libcli/smb2/negprot.c113
-rw-r--r--source4/libcli/smb2/notify.c114
-rw-r--r--source4/libcli/smb2/read.c87
-rw-r--r--source4/libcli/smb2/request.c737
-rw-r--r--source4/libcli/smb2/session.c274
-rw-r--r--source4/libcli/smb2/setinfo.c122
-rw-r--r--source4/libcli/smb2/signing.c117
-rw-r--r--source4/libcli/smb2/smb2.h303
-rw-r--r--source4/libcli/smb2/smb2_calls.h111
-rw-r--r--source4/libcli/smb2/tcon.c112
-rw-r--r--source4/libcli/smb2/tdis.c65
-rw-r--r--source4/libcli/smb2/transport.c417
-rw-r--r--source4/libcli/smb2/util.c216
-rw-r--r--source4/libcli/smb2/write.c81
-rw-r--r--source4/libcli/smb_composite/appendacl.c313
-rw-r--r--source4/libcli/smb_composite/connect.c520
-rw-r--r--source4/libcli/smb_composite/fetchfile.c192
-rw-r--r--source4/libcli/smb_composite/fsinfo.c210
-rw-r--r--source4/libcli/smb_composite/loadfile.c293
-rw-r--r--source4/libcli/smb_composite/savefile.c288
-rw-r--r--source4/libcli/smb_composite/sesssetup.c570
-rw-r--r--source4/libcli/smb_composite/smb2.c370
-rw-r--r--source4/libcli/smb_composite/smb_composite.h195
-rw-r--r--source4/libcli/smbc/README1
-rw-r--r--source4/libcli/util/clilsa.c359
-rw-r--r--source4/libcli/util/errormap.c1408
-rw-r--r--source4/libcli/util/nterr.c898
-rw-r--r--source4/libcli/util/pyerrors.h45
-rw-r--r--source4/libcli/wbclient/config.mk5
-rw-r--r--source4/libcli/wbclient/wbclient.c210
-rw-r--r--source4/libcli/wbclient/wbclient.h50
-rw-r--r--source4/libcli/wrepl/winsrepl.c871
-rw-r--r--source4/libcli/wrepl/winsrepl.h162
129 files changed, 37934 insertions, 0 deletions
diff --git a/source4/libcli/auth/config.mk b/source4/libcli/auth/config.mk
new file mode 100644
index 0000000000..498c2af258
--- /dev/null
+++ b/source4/libcli/auth/config.mk
@@ -0,0 +1,17 @@
+#################################
+# Start SUBSYSTEM LIBCLI_AUTH
+[SUBSYSTEM::LIBCLI_AUTH]
+PUBLIC_DEPENDENCIES = \
+ MSRPC_PARSE \
+ LIBSAMBA-HOSTCONFIG
+# End SUBSYSTEM LIBCLI_AUTH
+#################################
+
+LIBCLI_AUTH_OBJ_FILES = $(addprefix $(libclisrcdir)/auth/, \
+ credentials.o \
+ session.o \
+ smbencrypt.o \
+ smbdes.o)
+
+PUBLIC_HEADERS += $(libclisrcdir)/auth/credentials.h
+$(eval $(call proto_header_template,$(libclisrcdir)/auth/proto.h,$(LIBCLI_AUTH_OBJ_FILES:.o=.c)))
diff --git a/source4/libcli/auth/credentials.c b/source4/libcli/auth/credentials.c
new file mode 100644
index 0000000000..3c77b0836d
--- /dev/null
+++ b/source4/libcli/auth/credentials.c
@@ -0,0 +1,375 @@
+/*
+ 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 "auth/auth.h"
+#include "../lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+
+/*
+ initialise the credentials state for old-style 64 bit session keys
+
+ this call is made after the netr_ServerReqChallenge call
+*/
+static void creds_init_64bit(struct 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];
+
+ 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_STRUCT(creds->session_key);
+
+ des_crypt128(creds->session_key, sum2, machine_password->hash);
+
+ des_crypt112(creds->client.data, client_challenge->data, creds->session_key, 1);
+ des_crypt112(creds->server.data, server_challenge->data, creds->session_key, 1);
+
+ creds->seed = creds->client;
+}
+
+/*
+ initialise the credentials state for ADS-style 128 bit session keys
+
+ this call is made after the netr_ServerReqChallenge call
+*/
+static void creds_init_128bit(struct creds_CredentialState *creds,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const struct samr_Password *machine_password)
+{
+ unsigned char zero[4], tmp[16];
+ HMACMD5Context ctx;
+ struct MD5Context md5;
+
+ ZERO_STRUCT(creds->session_key);
+
+ memset(zero, 0, sizeof(zero));
+
+ hmac_md5_init_rfc2104(machine_password->hash, sizeof(machine_password->hash), &ctx);
+ MD5Init(&md5);
+ MD5Update(&md5, zero, sizeof(zero));
+ MD5Update(&md5, client_challenge->data, 8);
+ MD5Update(&md5, server_challenge->data, 8);
+ MD5Final(tmp, &md5);
+ hmac_md5_update(tmp, sizeof(tmp), &ctx);
+ hmac_md5_final(creds->session_key, &ctx);
+
+ creds->client = *client_challenge;
+ creds->server = *server_challenge;
+
+ des_crypt112(creds->client.data, client_challenge->data, creds->session_key, 1);
+ des_crypt112(creds->server.data, server_challenge->data, creds->session_key, 1);
+
+ creds->seed = creds->client;
+}
+
+
+/*
+ step the credentials to the next element in the chain, updating the
+ current client and server credentials and the seed
+*/
+static void creds_step(struct creds_CredentialState *creds)
+{
+ struct netr_Credential time_cred;
+
+ 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)));
+
+ des_crypt112(creds->client.data, time_cred.data, creds->session_key, 1);
+
+ 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)));
+
+ des_crypt112(creds->server.data, time_cred.data, creds->session_key, 1);
+
+ DEBUG(5,("\tSERVER %08x:%08x\n",
+ IVAL(creds->server.data, 0), IVAL(creds->server.data, 4)));
+
+ creds->seed = time_cred;
+}
+
+
+/*
+ DES encrypt a 8 byte LMSessionKey buffer using the Netlogon session key
+*/
+void creds_des_encrypt_LMKey(struct creds_CredentialState *creds, struct netr_LMSessionKey *key)
+{
+ struct netr_LMSessionKey tmp;
+ des_crypt56(tmp.key, key->key, creds->session_key, 1);
+ *key = tmp;
+}
+
+/*
+ DES decrypt a 8 byte LMSessionKey buffer using the Netlogon session key
+*/
+void creds_des_decrypt_LMKey(struct creds_CredentialState *creds, struct netr_LMSessionKey *key)
+{
+ struct netr_LMSessionKey tmp;
+ des_crypt56(tmp.key, key->key, creds->session_key, 0);
+ *key = tmp;
+}
+
+/*
+ DES encrypt a 16 byte password buffer using the session key
+*/
+void creds_des_encrypt(struct creds_CredentialState *creds, struct samr_Password *pass)
+{
+ struct samr_Password tmp;
+ des_crypt112_16(tmp.hash, pass->hash, creds->session_key, 1);
+ *pass = tmp;
+}
+
+/*
+ DES decrypt a 16 byte password buffer using the session key
+*/
+void creds_des_decrypt(struct creds_CredentialState *creds, struct samr_Password *pass)
+{
+ struct samr_Password tmp;
+ des_crypt112_16(tmp.hash, pass->hash, creds->session_key, 0);
+ *pass = tmp;
+}
+
+/*
+ ARCFOUR encrypt/decrypt a password buffer using the session key
+*/
+void creds_arcfour_crypt(struct creds_CredentialState *creds, uint8_t *data, size_t len)
+{
+ DATA_BLOB session_key = data_blob(creds->session_key, 16);
+
+ arcfour_crypt_blob(data, len, &session_key);
+
+ data_blob_free(&session_key);
+}
+
+/*****************************************************************
+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
+*/
+void creds_client_init(struct creds_CredentialState *creds,
+ 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)
+{
+ creds->sequence = time(NULL);
+ creds->negotiate_flags = negotiate_flags;
+
+ 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_128BIT) {
+ creds_init_128bit(creds, client_challenge, server_challenge, machine_password);
+ } else {
+ creds_init_64bit(creds, client_challenge, server_challenge, machine_password);
+ }
+
+ dump_data_pw("Session key", creds->session_key, 16);
+ dump_data_pw("Credential ", creds->client.data, 8);
+
+ *initial_credential = creds->client;
+}
+
+/*
+ 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
+*/
+void creds_client_authenticator(struct creds_CredentialState *creds,
+ struct netr_Authenticator *next)
+{
+ creds->sequence += 2;
+ creds_step(creds);
+
+ next->cred = creds->client;
+ next->timestamp = creds->sequence;
+}
+
+/*
+ check that a credentials reply from a server is correct
+*/
+bool creds_client_check(struct creds_CredentialState *creds,
+ const struct netr_Credential *received_credentials)
+{
+ if (!received_credentials ||
+ memcmp(received_credentials->data, creds->server.data, 8) != 0) {
+ 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
+******************************************************************/
+
+/*
+ initialise the credentials chain and return the first server
+ credentials
+*/
+void creds_server_init(struct creds_CredentialState *creds,
+ 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)
+{
+ if (negotiate_flags & NETLOGON_NEG_128BIT) {
+ creds_init_128bit(creds, client_challenge, server_challenge,
+ machine_password);
+ } else {
+ creds_init_64bit(creds, client_challenge, server_challenge,
+ machine_password);
+ }
+
+ *initial_credential = creds->server;
+ creds->negotiate_flags = negotiate_flags;
+}
+
+/*
+ check that a credentials reply from a server is correct
+*/
+bool creds_server_check(const struct creds_CredentialState *creds,
+ const struct netr_Credential *received_credentials)
+{
+ if (memcmp(received_credentials->data, creds->client.data, 8) != 0) {
+ 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;
+}
+
+NTSTATUS creds_server_step_check(struct creds_CredentialState *creds,
+ struct netr_Authenticator *received_authenticator,
+ struct netr_Authenticator *return_authenticator)
+{
+ if (!received_authenticator || !return_authenticator) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!creds) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* TODO: this may allow the a replay attack on a non-signed
+ connection. Should we check that this is increasing? */
+ creds->sequence = received_authenticator->timestamp;
+ creds_step(creds);
+ if (creds_server_check(creds, &received_authenticator->cred)) {
+ return_authenticator->cred = creds->server;
+ return_authenticator->timestamp = creds->sequence;
+ return NT_STATUS_OK;
+ } else {
+ ZERO_STRUCTP(return_authenticator);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+}
+
+void creds_decrypt_samlogon(struct creds_CredentialState *creds,
+ uint16_t validation_level,
+ union netr_Validation *validation)
+{
+ static const char zeros[16];
+
+ struct netr_SamBaseInfo *base = NULL;
+ 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;
+ }
+
+ if (!base) {
+ return;
+ }
+
+ /* 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_ARCFOUR) {
+ if (memcmp(base->key.key, zeros,
+ sizeof(base->key.key)) != 0) {
+ creds_arcfour_crypt(creds,
+ base->key.key,
+ sizeof(base->key.key));
+ }
+
+ if (memcmp(base->LMSessKey.key, zeros,
+ sizeof(base->LMSessKey.key)) != 0) {
+ creds_arcfour_crypt(creds,
+ base->LMSessKey.key,
+ sizeof(base->LMSessKey.key));
+ }
+ } else {
+ if (memcmp(base->LMSessKey.key, zeros,
+ sizeof(base->LMSessKey.key)) != 0) {
+ creds_des_decrypt_LMKey(creds,
+ &base->LMSessKey);
+ }
+ }
+}
diff --git a/source4/libcli/auth/credentials.h b/source4/libcli/auth/credentials.h
new file mode 100644
index 0000000000..4e11cb090f
--- /dev/null
+++ b/source4/libcli/auth/credentials.h
@@ -0,0 +1,46 @@
+/*
+ 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"
+
+struct creds_CredentialState {
+ uint32_t negotiate_flags;
+ uint8_t session_key[16];
+ uint32_t sequence;
+ struct netr_Credential seed;
+ struct netr_Credential client;
+ struct netr_Credential server;
+ uint16_t secure_channel_type;
+ const char *domain;
+ const char *computer_name;
+ const char *account_name;
+ struct dom_sid *sid;
+};
+
+/* for the timebeing, use the same neg flags as Samba3. */
+/* 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
+
+/* these are the flags that ADS clients use */
+#define NETLOGON_NEG_AUTH2_ADS_FLAGS (0x200fbffb | NETLOGON_NEG_ARCFOUR | NETLOGON_NEG_128BIT | NETLOGON_NEG_SCHANNEL)
+
+
diff --git a/source4/libcli/auth/libcli_auth.h b/source4/libcli/auth/libcli_auth.h
new file mode 100644
index 0000000000..ec1c1e7d98
--- /dev/null
+++ b/source4/libcli/auth/libcli_auth.h
@@ -0,0 +1,24 @@
+/*
+ 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 "libcli/auth/credentials.h"
+#include "libcli/auth/proto.h"
+
+#endif /* __LIBCLI_AUTH_H__ */
diff --git a/source4/libcli/auth/session.c b/source4/libcli/auth/session.c
new file mode 100644
index 0000000000..10c728662d
--- /dev/null
+++ b/source4/libcli/auth/session.c
@@ -0,0 +1,218 @@
+/*
+ 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
+*/
+void sess_crypt_blob(DATA_BLOB *out, const DATA_BLOB *in, const DATA_BLOB *session_key,
+ bool forward)
+{
+ int i, k;
+
+ for (i=0,k=0;
+ i<in->length;
+ i += 8, k += 7) {
+ uint8_t bin[8], bout[8], key[7];
+
+ memset(bin, 0, 8);
+ memcpy(bin, &in->data[i], MIN(8, in->length-i));
+
+ if (k + 7 > session_key->length) {
+ k = (session_key->length - k);
+ }
+ memcpy(key, &session_key->data[k], 7);
+
+ des_crypt56(bout, bin, key, forward?1:0);
+
+ memcpy(&out->data[i], bout, MIN(8, in->length-i));
+ }
+}
+
+
+/*
+ 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;
+
+ 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);
+
+ sess_crypt_blob(&ret, &src, session_key, true);
+
+ data_blob_free(&src);
+
+ 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 slen;
+ char *ret;
+
+ if (blob->length < 8) {
+ return NULL;
+ }
+
+ out = data_blob_talloc(mem_ctx, NULL, blob->length);
+ if (!out.data) {
+ return NULL;
+ }
+
+ sess_crypt_blob(&out, blob, session_key, false);
+
+ 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;
+
+ 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);
+
+ sess_crypt_blob(&ret, &src, session_key, true);
+
+ data_blob_free(&src);
+
+ 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 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;
+ }
+
+ sess_crypt_blob(&out, blob, session_key, false);
+
+ 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/source4/libcli/auth/smbdes.c b/source4/libcli/auth/smbdes.c
new file mode 100644
index 0000000000..32e65e779d
--- /dev/null
+++ b/source4/libcli/auth/smbdes.c
@@ -0,0 +1,381 @@
+/*
+ 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"
+
+/* NOTES:
+
+ This code makes no attempt to be fast! In fact, it is a very
+ slow implementation
+
+ This code is NOT a complete DES implementation. It implements only
+ the minimum necessary for SMB authentication, as used by all SMB
+ products (including every copy of Microsoft Windows95 ever sold)
+
+ In particular, it can only do a unchained forward DES pass. This
+ means it is not possible to use this code for encryption/decryption
+ of data, instead it is only useful as a "hash" algorithm.
+
+ There is no entry point into this code that allows normal DES operation.
+
+ I believe this means that this code does not come under ITAR
+ regulations but this is NOT a legal opinion. If you are concerned
+ about the applicability of ITAR regulations to this code then you
+ should confirm it for yourself (and maybe let me know if you come
+ up with a different answer to the one above)
+*/
+
+
+static const uint8_t perm1[56] = {57, 49, 41, 33, 25, 17, 9,
+ 1, 58, 50, 42, 34, 26, 18,
+ 10, 2, 59, 51, 43, 35, 27,
+ 19, 11, 3, 60, 52, 44, 36,
+ 63, 55, 47, 39, 31, 23, 15,
+ 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29,
+ 21, 13, 5, 28, 20, 12, 4};
+
+static const uint8_t perm2[48] = {14, 17, 11, 24, 1, 5,
+ 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8,
+ 16, 7, 27, 20, 13, 2,
+ 41, 52, 31, 37, 47, 55,
+ 30, 40, 51, 45, 33, 48,
+ 44, 49, 39, 56, 34, 53,
+ 46, 42, 50, 36, 29, 32};
+
+static const uint8_t perm3[64] = {58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6,
+ 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7};
+
+static const uint8_t perm4[48] = { 32, 1, 2, 3, 4, 5,
+ 4, 5, 6, 7, 8, 9,
+ 8, 9, 10, 11, 12, 13,
+ 12, 13, 14, 15, 16, 17,
+ 16, 17, 18, 19, 20, 21,
+ 20, 21, 22, 23, 24, 25,
+ 24, 25, 26, 27, 28, 29,
+ 28, 29, 30, 31, 32, 1};
+
+static const uint8_t perm5[32] = { 16, 7, 20, 21,
+ 29, 12, 28, 17,
+ 1, 15, 23, 26,
+ 5, 18, 31, 10,
+ 2, 8, 24, 14,
+ 32, 27, 3, 9,
+ 19, 13, 30, 6,
+ 22, 11, 4, 25};
+
+
+static const uint8_t perm6[64] ={ 40, 8, 48, 16, 56, 24, 64, 32,
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25};
+
+
+static const uint8_t sc[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
+
+static const uint8_t sbox[8][4][16] = {
+ {{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
+ {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},
+ {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},
+ {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}},
+
+ {{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
+ {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},
+ {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},
+ {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}},
+
+ {{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
+ {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},
+ {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},
+ {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}},
+
+ {{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
+ {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},
+ {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},
+ {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}},
+
+ {{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
+ {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},
+ {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},
+ {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}},
+
+ {{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
+ {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},
+ {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},
+ {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}},
+
+ {{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
+ {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},
+ {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},
+ {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}},
+
+ {{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
+ {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},
+ {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},
+ {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}};
+
+static void permute(char *out, const char *in, const uint8_t *p, int n)
+{
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = in[p[i]-1];
+}
+
+static void lshift(char *d, int count, int n)
+{
+ char out[64];
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = d[(i+count)%n];
+ for (i=0;i<n;i++)
+ d[i] = out[i];
+}
+
+static void concat(char *out, char *in1, char *in2, int l1, int l2)
+{
+ while (l1--)
+ *out++ = *in1++;
+ while (l2--)
+ *out++ = *in2++;
+}
+
+static void xor(char *out, char *in1, char *in2, int n)
+{
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = in1[i] ^ in2[i];
+}
+
+static void dohash(char *out, char *in, char *key, int forw)
+{
+ int i, j, k;
+ char pk1[56];
+ char c[28];
+ char d[28];
+ char cd[56];
+ char ki[16][48];
+ char pd1[64];
+ char l[32], r[32];
+ char rl[64];
+
+ permute(pk1, key, perm1, 56);
+
+ for (i=0;i<28;i++)
+ c[i] = pk1[i];
+ for (i=0;i<28;i++)
+ d[i] = pk1[i+28];
+
+ for (i=0;i<16;i++) {
+ lshift(c, sc[i], 28);
+ lshift(d, sc[i], 28);
+
+ concat(cd, c, d, 28, 28);
+ permute(ki[i], cd, perm2, 48);
+ }
+
+ permute(pd1, in, perm3, 64);
+
+ for (j=0;j<32;j++) {
+ l[j] = pd1[j];
+ r[j] = pd1[j+32];
+ }
+
+ for (i=0;i<16;i++) {
+ char er[48];
+ char erk[48];
+ char b[8][6];
+ char cb[32];
+ char pcb[32];
+ char r2[32];
+
+ permute(er, r, perm4, 48);
+
+ xor(erk, er, ki[forw ? i : 15 - i], 48);
+
+ for (j=0;j<8;j++)
+ for (k=0;k<6;k++)
+ b[j][k] = erk[j*6 + k];
+
+ for (j=0;j<8;j++) {
+ int m, n;
+ m = (b[j][0]<<1) | b[j][5];
+
+ n = (b[j][1]<<3) | (b[j][2]<<2) | (b[j][3]<<1) | b[j][4];
+
+ for (k=0;k<4;k++)
+ b[j][k] = (sbox[j][m][n] & (1<<(3-k)))?1:0;
+ }
+
+ for (j=0;j<8;j++)
+ for (k=0;k<4;k++)
+ cb[j*4+k] = b[j][k];
+ permute(pcb, cb, perm5, 32);
+
+ xor(r2, l, pcb, 32);
+
+ for (j=0;j<32;j++)
+ l[j] = r[j];
+
+ for (j=0;j<32;j++)
+ r[j] = r2[j];
+ }
+
+ concat(rl, r, l, 32, 32);
+
+ permute(out, rl, perm6, 64);
+}
+
+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);
+ }
+}
+
+/*
+ basic des crypt using a 56 bit (7 byte) key
+*/
+void des_crypt56(uint8_t out[8], const uint8_t in[8], const uint8_t key[7], int forw)
+{
+ int i;
+ char outb[64];
+ char inb[64];
+ char keyb[64];
+ uint8_t key2[8];
+
+ str_to_key(key, key2);
+
+ for (i=0;i<64;i++) {
+ inb[i] = (in[i/8] & (1<<(7-(i%8)))) ? 1 : 0;
+ keyb[i] = (key2[i/8] & (1<<(7-(i%8)))) ? 1 : 0;
+ outb[i] = 0;
+ }
+
+ dohash(outb, inb, keyb, forw);
+
+ for (i=0;i<8;i++) {
+ out[i] = 0;
+ }
+
+ for (i=0;i<64;i++) {
+ if (outb[i])
+ out[i/8] |= (1<<(7-(i%8)));
+ }
+}
+
+void E_P16(const uint8_t *p14,uint8_t *p16)
+{
+ const uint8_t sp8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
+ des_crypt56(p16, sp8, p14, 1);
+ des_crypt56(p16+8, sp8, p14+7, 1);
+}
+
+void E_P24(const uint8_t *p21, const uint8_t *c8, uint8_t *p24)
+{
+ des_crypt56(p24, c8, p21, 1);
+ des_crypt56(p24+8, c8, p21+7, 1);
+ des_crypt56(p24+16, c8, p21+14, 1);
+}
+
+void D_P16(const uint8_t *p14, const uint8_t *in, uint8_t *out)
+{
+ des_crypt56(out, in, p14, 0);
+ des_crypt56(out+8, in+8, p14+7, 0);
+}
+
+void E_old_pw_hash( uint8_t *p14, const uint8_t *in, uint8_t *out)
+{
+ des_crypt56(out, in, p14, 1);
+ des_crypt56(out+8, in+8, p14+7, 1);
+}
+
+/* des encryption with a 128 bit key */
+void des_crypt128(uint8_t out[8], const uint8_t in[8], const uint8_t key[16])
+{
+ uint8_t buf[8];
+ des_crypt56(buf, in, key, 1);
+ des_crypt56(out, buf, key+9, 1);
+}
+
+/* des encryption with a 64 bit key */
+void des_crypt64(uint8_t out[8], const uint8_t in[8], const uint8_t key[8], int forw)
+{
+ uint8_t buf[8];
+ uint8_t key2[8];
+ ZERO_STRUCT(key2);
+ des_crypt56(buf, in, key, forw);
+ key2[0] = key[7];
+ des_crypt56(out, buf, key2, forw);
+}
+
+/* des encryption with a 112 bit (14 byte) key */
+void des_crypt112(uint8_t out[8], const uint8_t in[8], const uint8_t key[14], int forw)
+{
+ uint8_t buf[8];
+ des_crypt56(buf, in, key, forw);
+ des_crypt56(out, buf, key+7, forw);
+}
+
+/* des encryption of a 16 byte lump of data with a 112 bit key */
+void des_crypt112_16(uint8_t out[16], uint8_t in[16], const uint8_t key[14], int forw)
+{
+ des_crypt56(out, in, key, forw);
+ des_crypt56(out + 8, in + 8, key+7, forw);
+}
+
+/* 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. */
+void sam_rid_crypt(uint_t rid, const uint8_t *in, uint8_t *out, int forw)
+{
+ uint8_t s[14];
+
+ 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);
+
+ des_crypt56(out, in, s, forw);
+ des_crypt56(out+8, in+8, s+7, forw);
+}
diff --git a/source4/libcli/auth/smbencrypt.c b/source4/libcli/auth/smbencrypt.c
new file mode 100644
index 0000000000..c6118c6568
--- /dev/null
+++ b/source4/libcli/auth/smbencrypt.c
@@ -0,0 +1,595 @@
+/*
+ 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 "auth/ntlmssp/ntlmssp.h"
+#include "auth/ntlmssp/msrpc_parse.h"
+#include "../lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+
+/*
+ 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 p21[21];
+
+ memset(p21,'\0',21);
+ ret = E_deshash(passwd, p21);
+
+ SMBOWFencrypt(p21, c8, p24);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("SMBencrypt: lm#, challenge, response\n"));
+ dump_data(100, p21, 16);
+ dump_data(100, c8, 8);
+ dump_data(100, p24, 24);
+#endif
+
+ 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 = true;
+ char dospwd[256];
+ ZERO_STRUCT(dospwd);
+
+ /* Password must be converted to DOS charset - null terminated, uppercase. */
+ push_string(dospwd, passwd, sizeof(dospwd), STR_ASCII|STR_UPPER|STR_TERMINATE);
+
+ /* Only the first 14 chars are considered, password need not be null terminated. */
+ E_P16((const uint8_t *)dospwd, p16);
+
+ if (strlen(dospwd) > 14) {
+ ret = false;
+ }
+
+ ZERO_STRUCT(dospwd);
+
+ return ret;
+}
+
+/* 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,
+ bool upper_case_domain, /* Transform the domain into UPPER case */
+ uint8_t kr_buf[16])
+{
+ smb_ucs2_t *user;
+ smb_ucs2_t *domain;
+ size_t user_byte_len;
+ size_t domain_byte_len;
+ bool ret;
+
+ HMACMD5Context ctx;
+ 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;
+ }
+
+ if (upper_case_domain) {
+ domain_in = strupper_talloc(mem_ctx, domain_in);
+ if (domain_in == NULL) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ ret = push_ucs2_talloc(mem_ctx, &user, user_in, &user_byte_len );
+ if (!ret) {
+ DEBUG(0, ("push_uss2_talloc() for user returned -1 (probably talloc() failure)\n"));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ret = push_ucs2_talloc(mem_ctx, &domain, domain_in, &domain_byte_len);
+ if (!ret) {
+ DEBUG(0, ("push_ucs2_talloc() for domain returned -1 (probably talloc() failure)\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;
+
+ hmac_md5_init_limK_to_64(owf, 16, &ctx);
+ hmac_md5_update((const void *)user, user_byte_len, &ctx);
+ hmac_md5_update((const void *)domain, domain_byte_len, &ctx);
+ hmac_md5_final(kr_buf, &ctx);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("ntv2_owf_gen: user, domain, owfkey, kr\n"));
+ dump_data(100, (const void *)user, user_byte_len);
+ dump_data(100, (const void *)domain, domain_byte_len);
+ dump_data(100, owf, 16);
+ dump_data(100, kr_buf, 16);
+#endif
+
+ talloc_free(mem_ctx);
+ return true;
+}
+
+/* Does the des encryption from the NT or LM MD4 hash. */
+void 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);
+ E_P24(p21, c8, p24);
+}
+
+/* Does the NT MD4 hash then des encryption. */
+
+void SMBNTencrypt(const char *passwd, uint8_t *c8, uint8_t *p24)
+{
+ uint8_t p21[21];
+
+ memset(p21,'\0',21);
+
+ E_md4hash(passwd, p21);
+ 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
+}
+
+/* Does the md5 encryption from the Key Response for NTLMv2. */
+void SMBOWFencrypt_ntv2(const uint8_t kr[16],
+ const DATA_BLOB *srv_chal,
+ const DATA_BLOB *smbcli_chal,
+ uint8_t resp_buf[16])
+{
+ HMACMD5Context ctx;
+
+ hmac_md5_init_limK_to_64(kr, 16, &ctx);
+ hmac_md5_update(srv_chal->data, srv_chal->length, &ctx);
+ hmac_md5_update(smbcli_chal->data, smbcli_chal->length, &ctx);
+ hmac_md5_final(resp_buf, &ctx);
+
+#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
+}
+
+void SMBsesskeygen_ntv2(const uint8_t kr[16],
+ const uint8_t * nt_resp, uint8_t sess_key[16])
+{
+ /* a very nice, 128 bit, variable session key */
+
+ HMACMD5Context ctx;
+
+ hmac_md5_init_limK_to_64(kr, 16, &ctx);
+ hmac_md5_update(nt_resp, 16, &ctx);
+ hmac_md5_final((uint8_t *)sess_key, &ctx);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_ntv2:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+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
+}
+
+void 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];
+
+ memcpy(partial_lm_hash, lm_hash, 8);
+ memset(partial_lm_hash + 8, 0xbd, 6);
+
+ des_crypt56(p24, lm_resp, partial_lm_hash, 1);
+ des_crypt56(p24+8, lm_resp, partial_lm_hash + 7, 1);
+
+ memcpy(sess_key, p24, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_lm_sess_key: \n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+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);
+
+ msrpc_gen(mem_ctx, &names_blob,
+ "aaa",
+ NTLMSSP_NAME_TYPE_DOMAIN, domain,
+ NTLMSSP_NAME_TYPE_SERVER, hostname,
+ 0, "");
+ return names_blob;
+}
+
+static DATA_BLOB NTLMv2_generate_client_data(TALLOC_CTX *mem_ctx, const DATA_BLOB *names_blob)
+{
+ uint8_t client_chal[8];
+ DATA_BLOB response = data_blob(NULL, 0);
+ uint8_t long_date[8];
+ NTTIME nttime;
+
+ unix_to_nt_time(&nttime, time(NULL));
+
+ 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 */
+
+ 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,
+ const DATA_BLOB *names_blob)
+{
+ uint8_t ntlmv2_response[16];
+ DATA_BLOB ntlmv2_client_data;
+ DATA_BLOB final_response;
+
+ 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, names_blob);
+
+ /* Given that data, and the challenge from the server, generate a response */
+ SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, &ntlmv2_client_data, ntlmv2_response);
+
+ 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);
+
+ /* 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 */
+ SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, &lmv2_client_data, lmv2_response);
+ 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 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];
+
+ /* 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, true, ntlm_v2_hash)) {
+ return false;
+ }
+
+ if (nt_response) {
+ *nt_response = NTLMv2_generate_response(mem_ctx,
+ ntlm_v2_hash, server_chal,
+ 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 */
+ SMBsesskeygen_ntv2(ntlm_v2_hash, nt_response->data, user_session_key->data);
+ }
+ }
+
+ /* LMv2 */
+
+ if (lm_response) {
+ *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 */
+ SMBsesskeygen_ntv2(ntlm_v2_hash, lm_response->data, lm_session_key->data);
+ }
+ }
+
+ 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, names_blob,
+ lm_response, nt_response, lm_session_key, user_session_key);
+}
+
+/***********************************************************
+ 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)
+{
+ uint8_t new_pw[512];
+ size_t new_pw_len;
+
+ /* the incoming buffer can be any alignment. */
+ string_flags |= STR_NOALIGN;
+
+ new_pw_len = push_string(new_pw,
+ password,
+ sizeof(new_pw), string_flags);
+
+ memcpy(&buffer[512 - new_pw_len], new_pw, new_pw_len);
+
+ generate_random_buffer(buffer, 512 - new_pw_len);
+
+ /*
+ * The length of the new password is in the last 4 bytes of
+ * the data buffer.
+ */
+ SIVAL(buffer, 512, new_pw_len);
+ ZERO_STRUCT(new_pw);
+ 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(uint8_t in_buffer[516], char *new_pwrd,
+ int new_pwrd_size, int string_flags)
+{
+ int byte_len=0;
+ ssize_t converted_pw_len;
+
+ /* the incoming buffer can be any alignment. */
+ string_flags |= STR_NOALIGN;
+
+ /*
+ 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.
+ */
+
+ /* 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;
+ }
+
+ /* decode into the return buffer. Buffer length supplied */
+ converted_pw_len = pull_string(new_pwrd, &in_buffer[512 - byte_len], new_pwrd_size,
+ byte_len, string_flags);
+
+ if (converted_pw_len == -1) {
+ return false;
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("decode_pw_buffer: new_pwrd: "));
+ dump_data(100, (const uint8_t *)new_pwrd, converted_pw_len);
+ DEBUG(100,("multibyte len:%d\n", (int)converted_pw_len));
+ DEBUG(100,("original char len:%d\n", byte_len/2));
+#endif
+
+ return true;
+}
+
+/***********************************************************
+ 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], DATA_BLOB *password)
+{
+ if (password->length > 512) {
+ return false;
+ }
+
+ memcpy(&buffer[512 - password->length], password->data, password->length);
+
+ generate_random_buffer(buffer, 512 - password->length);
+
+ /*
+ * The length of the new password is in the last 4 bytes of
+ * the data buffer.
+ */
+ SIVAL(buffer, 512, 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;
+ }
+
+ return true;
+}
diff --git a/source4/libcli/cldap/cldap.c b/source4/libcli/cldap/cldap.c
new file mode 100644
index 0000000000..b18ba12b1f
--- /dev/null
+++ b/source4/libcli/cldap/cldap.c
@@ -0,0 +1,738 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ cldap client library
+
+ Copyright (C) Andrew Tridgell 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/>.
+*/
+
+/*
+ see RFC1798 for details of CLDAP
+
+ basic properties
+ - carried over UDP on port 389
+ - request and response matched by message ID
+ - request consists of only a single searchRequest element
+ - response can be in one of two forms
+ - a single searchResponse, followed by a searchResult
+ - a single searchResult
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/ldap/ldap.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "libcli/cldap/cldap.h"
+#include "lib/socket/socket.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+
+/*
+ destroy a pending request
+*/
+static int cldap_request_destructor(struct cldap_request *req)
+{
+ if (req->state == CLDAP_REQUEST_SEND) {
+ DLIST_REMOVE(req->cldap->send_queue, req);
+ }
+ if (!req->is_reply && req->message_id != 0) {
+ idr_remove(req->cldap->idr, req->message_id);
+ req->message_id = 0;
+ }
+ return 0;
+}
+
+/*
+ handle recv events on a cldap socket
+*/
+static void cldap_socket_recv(struct cldap_socket *cldap)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(cldap);
+ NTSTATUS status;
+ struct socket_address *src;
+ DATA_BLOB blob;
+ size_t nread, dsize;
+ struct asn1_data *asn1 = asn1_init(tmp_ctx);
+ struct ldap_message *ldap_msg;
+ struct cldap_request *req;
+
+ if (!asn1) return;
+
+ status = socket_pending(cldap->sock, &dsize);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ blob = data_blob_talloc(tmp_ctx, NULL, dsize);
+ if (blob.data == NULL) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ status = socket_recvfrom(cldap->sock, blob.data, blob.length, &nread,
+ tmp_ctx, &src);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+ blob.length = nread;
+
+ DEBUG(2,("Received cldap packet of length %d from %s:%d\n",
+ (int)blob.length, src->addr, src->port));
+
+ if (!asn1_load(asn1, blob)) {
+ DEBUG(2,("Failed to setup for asn.1 decode\n"));
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ ldap_msg = talloc(tmp_ctx, struct ldap_message);
+ if (ldap_msg == NULL) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ /* this initial decode is used to find the message id */
+ status = ldap_decode(asn1, NULL, ldap_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("Failed to decode ldap message: %s\n", nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ /* find the pending request */
+ req = idr_find(cldap->idr, ldap_msg->messageid);
+ if (req == NULL) {
+ if (cldap->incoming.handler) {
+ cldap->incoming.handler(cldap, ldap_msg, src);
+ } else {
+ DEBUG(2,("Mismatched cldap reply %u from %s:%d\n",
+ ldap_msg->messageid, src->addr, src->port));
+ }
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ req->asn1 = talloc_steal(req, asn1);
+ req->asn1->ofs = 0;
+
+ req->state = CLDAP_REQUEST_DONE;
+ talloc_free(req->te);
+
+ talloc_free(tmp_ctx);
+
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+/*
+ handle request timeouts
+*/
+static void cldap_request_timeout(struct tevent_context *event_ctx,
+ struct tevent_timer *te, struct timeval t,
+ void *private_data)
+{
+ struct cldap_request *req = talloc_get_type(private_data, struct cldap_request);
+
+ /* possibly try again */
+ if (req->num_retries != 0) {
+ size_t len = req->encoded.length;
+
+ req->num_retries--;
+
+ socket_sendto(req->cldap->sock, &req->encoded, &len,
+ req->dest);
+
+ req->te = event_add_timed(req->cldap->event_ctx, req,
+ timeval_current_ofs(req->timeout, 0),
+ cldap_request_timeout, req);
+ return;
+ }
+
+ req->state = CLDAP_REQUEST_ERROR;
+ req->status = NT_STATUS_IO_TIMEOUT;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+/*
+ handle send events on a cldap socket
+*/
+static void cldap_socket_send(struct cldap_socket *cldap)
+{
+ struct cldap_request *req;
+ NTSTATUS status;
+
+ while ((req = cldap->send_queue)) {
+ size_t len;
+
+ len = req->encoded.length;
+ status = socket_sendto(cldap->sock, &req->encoded, &len,
+ req->dest);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(0,("Failed to send cldap request of length %u to %s:%d\n",
+ (unsigned)req->encoded.length, req->dest->addr, req->dest->port));
+ DLIST_REMOVE(cldap->send_queue, req);
+ req->state = CLDAP_REQUEST_ERROR;
+ req->status = status;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) return;
+
+ DLIST_REMOVE(cldap->send_queue, req);
+
+ if (req->is_reply) {
+ talloc_free(req);
+ } else {
+ req->state = CLDAP_REQUEST_WAIT;
+
+ req->te = event_add_timed(cldap->event_ctx, req,
+ timeval_current_ofs(req->timeout, 0),
+ cldap_request_timeout, req);
+
+ EVENT_FD_READABLE(cldap->fde);
+ }
+ }
+
+ EVENT_FD_NOT_WRITEABLE(cldap->fde);
+ return;
+}
+
+
+/*
+ handle fd events on a cldap_socket
+*/
+static void cldap_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct cldap_socket *cldap = talloc_get_type(private_data, struct cldap_socket);
+ if (flags & EVENT_FD_WRITE) {
+ cldap_socket_send(cldap);
+ }
+ if (flags & EVENT_FD_READ) {
+ cldap_socket_recv(cldap);
+ }
+}
+
+/*
+ initialise a cldap_socket. The event_ctx is optional, if provided
+ then operations will use that event context
+*/
+struct cldap_socket *cldap_socket_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct smb_iconv_convenience *iconv_convenience)
+{
+ struct cldap_socket *cldap;
+ NTSTATUS status;
+
+ cldap = talloc(mem_ctx, struct cldap_socket);
+ if (cldap == NULL) goto failed;
+
+ cldap->event_ctx = talloc_reference(cldap, event_ctx);
+ if (cldap->event_ctx == NULL) goto failed;
+
+ cldap->idr = idr_init(cldap);
+ if (cldap->idr == NULL) goto failed;
+
+ status = socket_create("ip", SOCKET_TYPE_DGRAM, &cldap->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ talloc_steal(cldap, cldap->sock);
+
+ cldap->fde = event_add_fd(cldap->event_ctx, cldap,
+ socket_get_fd(cldap->sock), 0,
+ cldap_socket_handler, cldap);
+
+ cldap->send_queue = NULL;
+ cldap->incoming.handler = NULL;
+ cldap->iconv_convenience = iconv_convenience;
+
+ return cldap;
+
+failed:
+ talloc_free(cldap);
+ return NULL;
+}
+
+
+/*
+ setup a handler for incoming requests
+*/
+NTSTATUS cldap_set_incoming_handler(struct cldap_socket *cldap,
+ void (*handler)(struct cldap_socket *, struct ldap_message *,
+ struct socket_address *),
+ void *private_data)
+{
+ cldap->incoming.handler = handler;
+ cldap->incoming.private_data = private_data;
+ EVENT_FD_READABLE(cldap->fde);
+ return NT_STATUS_OK;
+}
+
+/*
+ queue a cldap request for send
+*/
+struct cldap_request *cldap_search_send(struct cldap_socket *cldap,
+ struct cldap_search *io)
+{
+ struct ldap_message *msg;
+ struct cldap_request *req;
+ struct ldap_SearchRequest *search;
+
+ req = talloc_zero(cldap, struct cldap_request);
+ if (req == NULL) goto failed;
+
+ req->cldap = cldap;
+ req->state = CLDAP_REQUEST_SEND;
+ req->timeout = io->in.timeout;
+ req->num_retries = io->in.retries;
+ req->is_reply = false;
+ req->asn1 = asn1_init(req);
+ if (!req->asn1) {
+ goto failed;
+ }
+
+ req->dest = socket_address_from_strings(req, cldap->sock->backend_name,
+ io->in.dest_address,
+ io->in.dest_port);
+ if (!req->dest) goto failed;
+
+ req->message_id = idr_get_new_random(cldap->idr, req, UINT16_MAX);
+ if (req->message_id == -1) goto failed;
+
+ talloc_set_destructor(req, cldap_request_destructor);
+
+ msg = talloc(req, struct ldap_message);
+ if (msg == NULL) goto failed;
+ msg->messageid = req->message_id;
+ msg->type = LDAP_TAG_SearchRequest;
+ msg->controls = NULL;
+ search = &msg->r.SearchRequest;
+
+ search->basedn = "";
+ search->scope = LDAP_SEARCH_SCOPE_BASE;
+ search->deref = LDAP_DEREFERENCE_NEVER;
+ search->timelimit = 0;
+ search->sizelimit = 0;
+ search->attributesonly = false;
+ search->num_attributes = str_list_length(io->in.attributes);
+ search->attributes = io->in.attributes;
+ search->tree = ldb_parse_tree(req, io->in.filter);
+ if (search->tree == NULL) {
+ goto failed;
+ }
+
+ if (!ldap_encode(msg, NULL, &req->encoded, req)) {
+ DEBUG(0,("Failed to encode cldap message to %s:%d\n",
+ req->dest->addr, req->dest->port));
+ goto failed;
+ }
+
+ DLIST_ADD_END(cldap->send_queue, req, struct cldap_request *);
+
+ EVENT_FD_WRITEABLE(cldap->fde);
+
+ return req;
+
+failed:
+ talloc_free(req);
+ return NULL;
+}
+
+
+/*
+ queue a cldap reply for send
+*/
+NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io)
+{
+ struct ldap_message *msg;
+ struct cldap_request *req;
+ DATA_BLOB blob1, blob2;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ req = talloc_zero(cldap, struct cldap_request);
+ if (req == NULL) goto failed;
+
+ req->cldap = cldap;
+ req->state = CLDAP_REQUEST_SEND;
+ req->is_reply = true;
+ req->asn1 = asn1_init(req);
+ if (!req->asn1) {
+ goto failed;
+ }
+
+ req->dest = io->dest;
+ if (talloc_reference(req, io->dest) == NULL) goto failed;
+
+ talloc_set_destructor(req, cldap_request_destructor);
+
+ msg = talloc(req, struct ldap_message);
+ if (msg == NULL) goto failed;
+ msg->messageid = io->messageid;
+ msg->controls = NULL;
+
+ if (io->response) {
+ msg->type = LDAP_TAG_SearchResultEntry;
+ msg->r.SearchResultEntry = *io->response;
+
+ if (!ldap_encode(msg, NULL, &blob1, req)) {
+ DEBUG(0,("Failed to encode cldap message to %s:%d\n",
+ req->dest->addr, req->dest->port));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto failed;
+ }
+ } else {
+ blob1 = data_blob(NULL, 0);
+ }
+
+ msg->type = LDAP_TAG_SearchResultDone;
+ msg->r.SearchResultDone = *io->result;
+
+ if (!ldap_encode(msg, NULL, &blob2, req)) {
+ DEBUG(0,("Failed to encode cldap message to %s:%d\n",
+ req->dest->addr, req->dest->port));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto failed;
+ }
+
+ req->encoded = data_blob_talloc(req, NULL, blob1.length + blob2.length);
+ if (req->encoded.data == NULL) goto failed;
+
+ memcpy(req->encoded.data, blob1.data, blob1.length);
+ memcpy(req->encoded.data+blob1.length, blob2.data, blob2.length);
+
+ DLIST_ADD_END(cldap->send_queue, req, struct cldap_request *);
+
+ EVENT_FD_WRITEABLE(cldap->fde);
+
+ return NT_STATUS_OK;
+
+failed:
+ talloc_free(req);
+ return status;
+}
+
+/*
+ receive a cldap reply
+*/
+NTSTATUS cldap_search_recv(struct cldap_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_search *io)
+{
+ struct ldap_message *ldap_msg;
+ NTSTATUS status;
+
+ if (req == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while (req->state < CLDAP_REQUEST_DONE) {
+ if (event_loop_once(req->cldap->event_ctx) != 0) {
+ talloc_free(req);
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ }
+
+ if (req->state == CLDAP_REQUEST_ERROR) {
+ status = req->status;
+ talloc_free(req);
+ return status;
+ }
+
+ ldap_msg = talloc(mem_ctx, struct ldap_message);
+ NT_STATUS_HAVE_NO_MEMORY(ldap_msg);
+
+ status = ldap_decode(req->asn1, NULL, ldap_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("Failed to decode cldap search reply: %s\n", nt_errstr(status)));
+ talloc_free(req);
+ return status;
+ }
+
+ ZERO_STRUCT(io->out);
+
+ /* the first possible form has a search result in first place */
+ if (ldap_msg->type == LDAP_TAG_SearchResultEntry) {
+ io->out.response = talloc(mem_ctx, struct ldap_SearchResEntry);
+ NT_STATUS_HAVE_NO_MEMORY(io->out.response);
+ *io->out.response = ldap_msg->r.SearchResultEntry;
+
+ /* decode the 2nd part */
+ status = ldap_decode(req->asn1, NULL, ldap_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("Failed to decode cldap search result entry: %s\n", nt_errstr(status)));
+ talloc_free(req);
+ return status;
+ }
+ }
+
+ if (ldap_msg->type != LDAP_TAG_SearchResultDone) {
+ talloc_free(req);
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
+
+ io->out.result = talloc(mem_ctx, struct ldap_Result);
+ NT_STATUS_HAVE_NO_MEMORY(io->out.result);
+ *io->out.result = ldap_msg->r.SearchResultDone;
+
+ talloc_free(req);
+
+ if (io->out.result->resultcode != LDAP_SUCCESS) {
+ return NT_STATUS_LDAP(io->out.result->resultcode);
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*
+ synchronous cldap search
+*/
+NTSTATUS cldap_search(struct cldap_socket *cldap,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_search *io)
+{
+ struct cldap_request *req = cldap_search_send(cldap, io);
+ return cldap_search_recv(req, mem_ctx, io);
+}
+
+
+
+/*
+ queue a cldap netlogon for send
+*/
+struct cldap_request *cldap_netlogon_send(struct cldap_socket *cldap,
+ struct cldap_netlogon *io)
+{
+ struct cldap_search search;
+ char *filter;
+ struct cldap_request *req;
+ const char *attr[] = { "NetLogon", NULL };
+ TALLOC_CTX *tmp_ctx = talloc_new(cldap);
+
+ filter = talloc_asprintf(tmp_ctx, "(&(NtVer=%s)",
+ ldap_encode_ndr_uint32(tmp_ctx, io->in.version));
+ if (filter == NULL) goto failed;
+ if (io->in.user) {
+ filter = talloc_asprintf_append_buffer(filter, "(User=%s)", io->in.user);
+ if (filter == NULL) goto failed;
+ }
+ if (io->in.host) {
+ filter = talloc_asprintf_append_buffer(filter, "(Host=%s)", io->in.host);
+ if (filter == NULL) goto failed;
+ }
+ if (io->in.realm) {
+ filter = talloc_asprintf_append_buffer(filter, "(DnsDomain=%s)", io->in.realm);
+ if (filter == NULL) goto failed;
+ }
+ if (io->in.acct_control != -1) {
+ filter = talloc_asprintf_append_buffer(filter, "(AAC=%s)",
+ ldap_encode_ndr_uint32(tmp_ctx, io->in.acct_control));
+ if (filter == NULL) goto failed;
+ }
+ if (io->in.domain_sid) {
+ struct dom_sid *sid = dom_sid_parse_talloc(tmp_ctx, io->in.domain_sid);
+ if (sid == NULL) goto failed;
+ filter = talloc_asprintf_append_buffer(filter, "(domainSid=%s)",
+ ldap_encode_ndr_dom_sid(tmp_ctx, sid));
+ if (filter == NULL) goto failed;
+ }
+ if (io->in.domain_guid) {
+ struct GUID guid;
+ NTSTATUS status;
+ status = GUID_from_string(io->in.domain_guid, &guid);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ filter = talloc_asprintf_append_buffer(filter, "(DomainGuid=%s)",
+ ldap_encode_ndr_GUID(tmp_ctx, &guid));
+ if (filter == NULL) goto failed;
+ }
+ filter = talloc_asprintf_append_buffer(filter, ")");
+ if (filter == NULL) goto failed;
+
+ search.in.dest_address = io->in.dest_address;
+ search.in.dest_port = io->in.dest_port;
+ search.in.filter = filter;
+ search.in.attributes = attr;
+ search.in.timeout = 2;
+ search.in.retries = 2;
+
+ req = cldap_search_send(cldap, &search);
+
+ talloc_free(tmp_ctx);
+ return req;
+failed:
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+
+/*
+ receive a cldap netlogon reply
+*/
+NTSTATUS cldap_netlogon_recv(struct cldap_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_netlogon *io)
+{
+ NTSTATUS status;
+ struct cldap_search search;
+ struct cldap_socket *cldap;
+ DATA_BLOB *data;
+
+ cldap = req->cldap;
+
+ status = cldap_search_recv(req, mem_ctx, &search);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (search.out.response == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ if (search.out.response->num_attributes != 1 ||
+ strcasecmp(search.out.response->attributes[0].name, "netlogon") != 0 ||
+ search.out.response->attributes[0].num_values != 1 ||
+ search.out.response->attributes[0].values->length < 2) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ data = search.out.response->attributes[0].values;
+
+ status = pull_netlogon_samlogon_response(data, mem_ctx, req->cldap->iconv_convenience,
+ &io->out.netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (io->in.map_response) {
+ map_netlogon_samlogon_response(&io->out.netlogon);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ sync cldap netlogon search
+*/
+NTSTATUS cldap_netlogon(struct cldap_socket *cldap,
+ TALLOC_CTX *mem_ctx, struct cldap_netlogon *io)
+{
+ struct cldap_request *req = cldap_netlogon_send(cldap, io);
+ return cldap_netlogon_recv(req, mem_ctx, io);
+}
+
+
+/*
+ send an empty reply (used on any error, so the client doesn't keep waiting
+ or send the bad request again)
+*/
+NTSTATUS cldap_empty_reply(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct socket_address *src)
+{
+ NTSTATUS status;
+ struct cldap_reply reply;
+ struct ldap_Result result;
+
+ reply.messageid = message_id;
+ reply.dest = src;
+ reply.response = NULL;
+ reply.result = &result;
+
+ ZERO_STRUCT(result);
+
+ status = cldap_reply_send(cldap, &reply);
+
+ return status;
+}
+
+/*
+ send an error reply (used on any error, so the client doesn't keep waiting
+ or send the bad request again)
+*/
+NTSTATUS cldap_error_reply(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct socket_address *src,
+ int resultcode,
+ const char *errormessage)
+{
+ NTSTATUS status;
+ struct cldap_reply reply;
+ struct ldap_Result result;
+
+ reply.messageid = message_id;
+ reply.dest = src;
+ reply.response = NULL;
+ reply.result = &result;
+
+ ZERO_STRUCT(result);
+ result.resultcode = resultcode;
+ result.errormessage = errormessage;
+
+ status = cldap_reply_send(cldap, &reply);
+
+ return status;
+}
+
+
+/*
+ send a netlogon reply
+*/
+NTSTATUS cldap_netlogon_reply(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct socket_address *src,
+ uint32_t version,
+ struct netlogon_samlogon_response *netlogon)
+{
+ NTSTATUS status;
+ struct cldap_reply reply;
+ struct ldap_SearchResEntry response;
+ struct ldap_Result result;
+ TALLOC_CTX *tmp_ctx = talloc_new(cldap);
+ DATA_BLOB blob;
+
+ status = push_netlogon_samlogon_response(&blob, tmp_ctx, cldap->iconv_convenience,
+ netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ reply.messageid = message_id;
+ reply.dest = src;
+ reply.response = &response;
+ reply.result = &result;
+
+ ZERO_STRUCT(result);
+
+ response.dn = "";
+ response.num_attributes = 1;
+ response.attributes = talloc(tmp_ctx, struct ldb_message_element);
+ NT_STATUS_HAVE_NO_MEMORY(response.attributes);
+ response.attributes->name = "netlogon";
+ response.attributes->num_values = 1;
+ response.attributes->values = &blob;
+
+ status = cldap_reply_send(cldap, &reply);
+
+ talloc_free(tmp_ctx);
+
+ return status;
+}
+
+
diff --git a/source4/libcli/cldap/cldap.h b/source4/libcli/cldap/cldap.h
new file mode 100644
index 0000000000..8951daa775
--- /dev/null
+++ b/source4/libcli/cldap/cldap.h
@@ -0,0 +1,182 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a async CLDAP library
+
+ Copyright (C) Andrew Tridgell 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 "../lib/util/asn1.h"
+#include "../libcli/netlogon.h"
+
+struct ldap_message;
+
+enum cldap_request_state {CLDAP_REQUEST_SEND,
+ CLDAP_REQUEST_WAIT,
+ CLDAP_REQUEST_DONE,
+ CLDAP_REQUEST_ERROR};
+
+/*
+ a cldap request packet
+*/
+struct cldap_request {
+ struct cldap_request *next, *prev;
+
+ struct cldap_socket *cldap;
+
+ enum cldap_request_state state;
+ NTSTATUS status;
+
+ /* where to send the request */
+ struct socket_address *dest;
+
+ /* timeout between retries (seconds) */
+ int timeout;
+ int num_retries;
+
+ bool is_reply;
+
+ /* the ldap message_id */
+ int message_id;
+
+ struct tevent_timer *te;
+
+ /* the encoded request */
+ DATA_BLOB encoded;
+
+ /* the reply data */
+ struct asn1_data *asn1;
+
+ /* information on what to do on completion */
+ struct {
+ void (*fn)(struct cldap_request *);
+ void *private_data;
+ } async;
+};
+
+/*
+ context structure for operations on cldap packets
+*/
+struct cldap_socket {
+ struct socket_context *sock;
+ struct tevent_context *event_ctx;
+ struct smb_iconv_convenience *iconv_convenience;
+
+ /* the fd event */
+ struct tevent_fd *fde;
+
+ /* a queue of outgoing requests */
+ struct cldap_request *send_queue;
+
+ /* mapping from message_id to pending request */
+ struct idr_context *idr;
+
+ /* what to do with incoming request packets */
+ struct {
+ void (*handler)(struct cldap_socket *, struct ldap_message *,
+ struct socket_address *);
+ void *private_data;
+ } incoming;
+};
+
+
+/*
+ a general cldap search request
+*/
+struct cldap_search {
+ struct {
+ const char *dest_address;
+ uint16_t dest_port;
+ const char *filter;
+ const char **attributes;
+ int timeout;
+ int retries;
+ } in;
+ struct {
+ struct ldap_SearchResEntry *response;
+ struct ldap_Result *result;
+ } out;
+};
+
+struct cldap_socket *cldap_socket_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct smb_iconv_convenience *iconv_convenience);
+NTSTATUS cldap_set_incoming_handler(struct cldap_socket *cldap,
+ void (*handler)(struct cldap_socket *, struct ldap_message *,
+ struct socket_address *),
+ void *private_data);
+struct cldap_request *cldap_search_send(struct cldap_socket *cldap,
+ struct cldap_search *io);
+NTSTATUS cldap_search_recv(struct cldap_request *req, TALLOC_CTX *mem_ctx,
+ struct cldap_search *io);
+NTSTATUS cldap_search(struct cldap_socket *cldap, TALLOC_CTX *mem_ctx,
+ struct cldap_search *io);
+
+
+/*
+ a general cldap reply
+*/
+struct cldap_reply {
+ uint32_t messageid;
+ struct socket_address *dest;
+ struct ldap_SearchResEntry *response;
+ struct ldap_Result *result;
+};
+
+NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io);
+
+NTSTATUS cldap_empty_reply(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct socket_address *src);
+NTSTATUS cldap_error_reply(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct socket_address *src,
+ int resultcode,
+ const char *errormessage);
+
+/*
+ a netlogon cldap request
+*/
+struct cldap_netlogon {
+ struct {
+ const char *dest_address;
+ uint16_t dest_port;
+ const char *realm;
+ const char *host;
+ const char *user;
+ const char *domain_guid;
+ const char *domain_sid;
+ int acct_control;
+ uint32_t version;
+ bool map_response;
+ } in;
+ struct {
+ struct netlogon_samlogon_response netlogon;
+ } out;
+};
+
+struct cldap_request *cldap_netlogon_send(struct cldap_socket *cldap,
+ struct cldap_netlogon *io);
+NTSTATUS cldap_netlogon_recv(struct cldap_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_netlogon *io);
+NTSTATUS cldap_netlogon(struct cldap_socket *cldap,
+ TALLOC_CTX *mem_ctx, struct cldap_netlogon *io);
+NTSTATUS cldap_netlogon_reply(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct socket_address *src,
+ uint32_t version,
+ struct netlogon_samlogon_response *netlogon);
diff --git a/source4/libcli/cliconnect.c b/source4/libcli/cliconnect.c
new file mode 100644
index 0000000000..14935dadba
--- /dev/null
+++ b/source4/libcli/cliconnect.c
@@ -0,0 +1,267 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ client connect/disconnect routines
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) James Peach 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 "includes.h"
+#include "libcli/libcli.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/*
+ wrapper around smbcli_sock_connect()
+*/
+bool smbcli_socket_connect(struct smbcli_state *cli, const char *server,
+ const char **ports,
+ struct tevent_context *ev_ctx,
+ struct resolve_context *resolve_ctx,
+ struct smbcli_options *options,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char *socket_options)
+{
+ struct smbcli_socket *sock;
+
+ sock = smbcli_sock_connect_byname(server, ports, NULL,
+ resolve_ctx, ev_ctx,
+ socket_options);
+
+ if (sock == NULL) return false;
+
+ cli->transport = smbcli_transport_init(sock, cli, true, options,
+ iconv_convenience);
+ if (!cli->transport) {
+ return false;
+ }
+
+ return true;
+}
+
+/* wrapper around smbcli_transport_connect() */
+bool smbcli_transport_establish(struct smbcli_state *cli,
+ struct nbt_name *calling,
+ struct nbt_name *called)
+{
+ return smbcli_transport_connect(cli->transport, calling, called);
+}
+
+/* wrapper around smb_raw_negotiate() */
+NTSTATUS smbcli_negprot(struct smbcli_state *cli, bool unicode, int maxprotocol)
+{
+ return smb_raw_negotiate(cli->transport, unicode, maxprotocol);
+}
+
+/* wrapper around smb_raw_sesssetup() */
+NTSTATUS smbcli_session_setup(struct smbcli_state *cli,
+ struct cli_credentials *credentials,
+ const char *workgroup,
+ struct smbcli_session_options options,
+ struct gensec_settings *gensec_settings)
+{
+ struct smb_composite_sesssetup setup;
+ NTSTATUS status;
+
+ cli->session = smbcli_session_init(cli->transport, cli, true,
+ options);
+ if (!cli->session) return NT_STATUS_UNSUCCESSFUL;
+
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities = cli->transport->negotiate.capabilities;
+ setup.in.credentials = credentials;
+ setup.in.workgroup = workgroup;
+ setup.in.gensec_settings = gensec_settings;
+
+ status = smb_composite_sesssetup(cli->session, &setup);
+
+ cli->session->vuid = setup.out.vuid;
+
+ return status;
+}
+
+/* wrapper around smb_raw_tcon() */
+NTSTATUS smbcli_tconX(struct smbcli_state *cli, const char *sharename,
+ const char *devtype, const char *password)
+{
+ union smb_tcon tcon;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ cli->tree = smbcli_tree_init(cli->session, cli, true);
+ if (!cli->tree) return NT_STATUS_UNSUCCESSFUL;
+
+ mem_ctx = talloc_init("tcon");
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* setup a tree connect */
+ tcon.generic.level = RAW_TCON_TCONX;
+ tcon.tconx.in.flags = 0;
+ if (cli->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) {
+ tcon.tconx.in.password = data_blob(NULL, 0);
+ } else if (cli->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
+ tcon.tconx.in.password = data_blob_talloc(mem_ctx, NULL, 24);
+ if (cli->transport->negotiate.secblob.length < 8) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ SMBencrypt(password, cli->transport->negotiate.secblob.data, tcon.tconx.in.password.data);
+ } else {
+ tcon.tconx.in.password = data_blob_talloc(mem_ctx, password, strlen(password)+1);
+ }
+ tcon.tconx.in.path = sharename;
+ tcon.tconx.in.device = devtype;
+
+ status = smb_raw_tcon(cli->tree, mem_ctx, &tcon);
+
+ cli->tree->tid = tcon.tconx.out.tid;
+
+ talloc_free(mem_ctx);
+
+ return status;
+}
+
+
+/*
+ easy way to get to a fully connected smbcli_state in one call
+*/
+NTSTATUS smbcli_full_connection(TALLOC_CTX *parent_ctx,
+ struct smbcli_state **ret_cli,
+ const char *host,
+ const char **ports,
+ const char *sharename,
+ const char *devtype,
+ const char *socket_options,
+ struct cli_credentials *credentials,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *ev,
+ struct smbcli_options *options,
+ struct smbcli_session_options *session_options,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct gensec_settings *gensec_settings)
+{
+ struct smbcli_tree *tree;
+ NTSTATUS status;
+
+ *ret_cli = NULL;
+
+ status = smbcli_tree_full_connection(parent_ctx,
+ &tree, host, ports,
+ sharename, devtype,
+ socket_options,
+ credentials, resolve_ctx, ev,
+ options,
+ session_options,
+ iconv_convenience,
+ gensec_settings);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ (*ret_cli) = smbcli_state_init(parent_ctx);
+
+ (*ret_cli)->tree = tree;
+ (*ret_cli)->session = tree->session;
+ (*ret_cli)->transport = tree->session->transport;
+
+ talloc_steal(*ret_cli, tree);
+
+done:
+ return status;
+}
+
+
+/*
+ disconnect the tree
+*/
+NTSTATUS smbcli_tdis(struct smbcli_state *cli)
+{
+ return smb_tree_disconnect(cli->tree);
+}
+
+/****************************************************************************
+ Initialise a client state structure.
+****************************************************************************/
+struct smbcli_state *smbcli_state_init(TALLOC_CTX *mem_ctx)
+{
+ return talloc_zero(mem_ctx, struct smbcli_state);
+}
+
+/* Insert a NULL at the first separator of the given path and return a pointer
+ * to the remainder of the string.
+ */
+static char *
+terminate_path_at_separator(char * path)
+{
+ char * p;
+
+ if (!path) {
+ return NULL;
+ }
+
+ if ((p = strchr_m(path, '/'))) {
+ *p = '\0';
+ return p + 1;
+ }
+
+ if ((p = strchr_m(path, '\\'))) {
+ *p = '\0';
+ return p + 1;
+ }
+
+ /* No separator. */
+ return NULL;
+}
+
+/*
+ parse a //server/share type UNC name
+*/
+bool smbcli_parse_unc(const char *unc_name, TALLOC_CTX *mem_ctx,
+ char **hostname, char **sharename)
+{
+ char *p;
+
+ *hostname = *sharename = NULL;
+
+ if (strncmp(unc_name, "\\\\", 2) &&
+ strncmp(unc_name, "//", 2)) {
+ return false;
+ }
+
+ *hostname = talloc_strdup(mem_ctx, &unc_name[2]);
+ p = terminate_path_at_separator(*hostname);
+
+ if (p != NULL && *p) {
+ *sharename = talloc_strdup(mem_ctx, p);
+ terminate_path_at_separator(*sharename);
+ }
+
+ if (*hostname && *sharename) {
+ return true;
+ }
+
+ talloc_free(*hostname);
+ talloc_free(*sharename);
+ *hostname = *sharename = NULL;
+ return false;
+}
+
+
+
diff --git a/source4/libcli/clideltree.c b/source4/libcli/clideltree.c
new file mode 100644
index 0000000000..d947ac3547
--- /dev/null
+++ b/source4/libcli/clideltree.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ useful function for deleting a whole directory tree
+ Copyright (C) Andrew Tridgell 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/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "system/dir.h"
+
+struct delete_state {
+ struct smbcli_tree *tree;
+ int total_deleted;
+ bool failed;
+};
+
+/*
+ callback function for torture_deltree()
+*/
+static void delete_fn(struct clilist_file_info *finfo, const char *name, void *state)
+{
+ struct delete_state *dstate = (struct delete_state *)state;
+ char *s, *n;
+ if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
+ return;
+ }
+
+ n = strdup(name);
+ n[strlen(n)-1] = 0;
+ asprintf(&s, "%s%s", n, finfo->name);
+
+ if (finfo->attrib & FILE_ATTRIBUTE_READONLY) {
+ if (NT_STATUS_IS_ERR(smbcli_setatr(dstate->tree, s, 0, 0))) {
+ DEBUG(2,("Failed to remove READONLY on %s - %s\n",
+ s, smbcli_errstr(dstate->tree)));
+ }
+ }
+
+ if (finfo->attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ char *s2;
+ asprintf(&s2, "%s\\*", s);
+ smbcli_unlink(dstate->tree, s2);
+ smbcli_list(dstate->tree, s2,
+ FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
+ delete_fn, state);
+ free(s2);
+ if (NT_STATUS_IS_ERR(smbcli_rmdir(dstate->tree, s))) {
+ DEBUG(2,("Failed to delete %s - %s\n",
+ s, smbcli_errstr(dstate->tree)));
+ dstate->failed = true;
+ }
+ dstate->total_deleted++;
+ } else {
+ if (NT_STATUS_IS_ERR(smbcli_unlink(dstate->tree, s))) {
+ DEBUG(2,("Failed to delete %s - %s\n",
+ s, smbcli_errstr(dstate->tree)));
+ dstate->failed = true;
+ }
+ dstate->total_deleted++;
+ }
+ free(s);
+ free(n);
+}
+
+/*
+ recursively descend a tree deleting all files
+ returns the number of files deleted, or -1 on error
+*/
+int smbcli_deltree(struct smbcli_tree *tree, const char *dname)
+{
+ char *mask;
+ struct delete_state dstate;
+ NTSTATUS status;
+
+ dstate.tree = tree;
+ dstate.total_deleted = 0;
+ dstate.failed = false;
+
+ /* it might be a file */
+ status = smbcli_unlink(tree, dname);
+ if (NT_STATUS_IS_OK(smbcli_unlink(tree, dname))) {
+ return 1;
+ }
+ if (NT_STATUS_EQUAL(smbcli_nt_error(tree), NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
+ NT_STATUS_EQUAL(smbcli_nt_error(tree), NT_STATUS_OBJECT_PATH_NOT_FOUND) ||
+ NT_STATUS_EQUAL(smbcli_nt_error(tree), NT_STATUS_NO_SUCH_FILE) ||
+ NT_STATUS_EQUAL(smbcli_nt_error(tree), NT_STATUS_DOS(ERRDOS, ERRbadfile))) {
+ return 0;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ status = smbcli_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL, 0);
+ if (NT_STATUS_IS_OK(smbcli_unlink(tree, dname))) {
+ return 1;
+ }
+ }
+
+ asprintf(&mask, "%s\\*", dname);
+ smbcli_unlink(dstate.tree, mask);
+ smbcli_list(dstate.tree, mask,
+ FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
+ delete_fn, &dstate);
+ free(mask);
+
+ status = smbcli_rmdir(dstate.tree, dname);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ status = smbcli_setatr(dstate.tree, dname, FILE_ATTRIBUTE_NORMAL, 0);
+ status = smbcli_rmdir(dstate.tree, dname);
+ }
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(2,("Failed to delete %s - %s\n",
+ dname, smbcli_errstr(dstate.tree)));
+ return -1;
+ }
+ dstate.total_deleted++;
+
+ if (dstate.failed) {
+ return -1;
+ }
+
+ return dstate.total_deleted;
+}
diff --git a/source4/libcli/clifile.c b/source4/libcli/clifile.c
new file mode 100644
index 0000000000..2cf174060b
--- /dev/null
+++ b/source4/libcli/clifile.c
@@ -0,0 +1,702 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file operations
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2001-2002
+ Copyright (C) James Myers 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/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+
+/****************************************************************************
+ Hard/Symlink a file (UNIX extensions).
+****************************************************************************/
+
+static NTSTATUS smbcli_link_internal(struct smbcli_tree *tree,
+ const char *fname_src,
+ const char *fname_dst, bool hard_link)
+{
+ union smb_setfileinfo parms;
+ NTSTATUS status;
+
+ if (hard_link) {
+ parms.generic.level = RAW_SFILEINFO_UNIX_HLINK;
+ parms.unix_hlink.in.file.path = fname_src;
+ parms.unix_hlink.in.link_dest = fname_dst;
+ } else {
+ parms.generic.level = RAW_SFILEINFO_UNIX_LINK;
+ parms.unix_link.in.file.path = fname_src;
+ parms.unix_link.in.link_dest = fname_dst;
+ }
+
+ status = smb_raw_setpathinfo(tree, &parms);
+
+ return status;
+}
+
+/****************************************************************************
+ Map standard UNIX permissions onto wire representations.
+****************************************************************************/
+uint32_t unix_perms_to_wire(mode_t perms)
+{
+ uint_t ret = 0;
+
+ ret |= ((perms & S_IXOTH) ? UNIX_X_OTH : 0);
+ ret |= ((perms & S_IWOTH) ? UNIX_W_OTH : 0);
+ ret |= ((perms & S_IROTH) ? UNIX_R_OTH : 0);
+ ret |= ((perms & S_IXGRP) ? UNIX_X_GRP : 0);
+ ret |= ((perms & S_IWGRP) ? UNIX_W_GRP : 0);
+ ret |= ((perms & S_IRGRP) ? UNIX_R_GRP : 0);
+ ret |= ((perms & S_IXUSR) ? UNIX_X_USR : 0);
+ ret |= ((perms & S_IWUSR) ? UNIX_W_USR : 0);
+ ret |= ((perms & S_IRUSR) ? UNIX_R_USR : 0);
+#ifdef S_ISVTX
+ ret |= ((perms & S_ISVTX) ? UNIX_STICKY : 0);
+#endif
+#ifdef S_ISGID
+ ret |= ((perms & S_ISGID) ? UNIX_SET_GID : 0);
+#endif
+#ifdef S_ISUID
+ ret |= ((perms & S_ISUID) ? UNIX_SET_UID : 0);
+#endif
+ return ret;
+}
+
+/****************************************************************************
+ Symlink a file (UNIX extensions).
+****************************************************************************/
+NTSTATUS smbcli_unix_symlink(struct smbcli_tree *tree, const char *fname_src,
+ const char *fname_dst)
+{
+ return smbcli_link_internal(tree, fname_src, fname_dst, false);
+}
+
+/****************************************************************************
+ Hard a file (UNIX extensions).
+****************************************************************************/
+NTSTATUS smbcli_unix_hardlink(struct smbcli_tree *tree, const char *fname_src,
+ const char *fname_dst)
+{
+ return smbcli_link_internal(tree, fname_src, fname_dst, true);
+}
+
+
+/****************************************************************************
+ Chmod or chown a file internal (UNIX extensions).
+****************************************************************************/
+static NTSTATUS smbcli_unix_chmod_chown_internal(struct smbcli_tree *tree,
+ const char *fname,
+ uint32_t mode, uint32_t uid,
+ uint32_t gid)
+{
+ union smb_setfileinfo parms;
+ NTSTATUS status;
+
+ parms.generic.level = SMB_SFILEINFO_UNIX_BASIC;
+ parms.unix_basic.in.file.path = fname;
+ parms.unix_basic.in.uid = uid;
+ parms.unix_basic.in.gid = gid;
+ parms.unix_basic.in.mode = mode;
+
+ status = smb_raw_setpathinfo(tree, &parms);
+
+ return status;
+}
+
+/****************************************************************************
+ chmod a file (UNIX extensions).
+****************************************************************************/
+
+NTSTATUS smbcli_unix_chmod(struct smbcli_tree *tree, const char *fname, mode_t mode)
+{
+ return smbcli_unix_chmod_chown_internal(tree, fname,
+ unix_perms_to_wire(mode),
+ SMB_UID_NO_CHANGE,
+ SMB_GID_NO_CHANGE);
+}
+
+/****************************************************************************
+ chown a file (UNIX extensions).
+****************************************************************************/
+NTSTATUS smbcli_unix_chown(struct smbcli_tree *tree, const char *fname, uid_t uid,
+ gid_t gid)
+{
+ return smbcli_unix_chmod_chown_internal(tree, fname, SMB_MODE_NO_CHANGE,
+ (uint32_t)uid, (uint32_t)gid);
+}
+
+
+/****************************************************************************
+ Rename a file.
+****************************************************************************/
+NTSTATUS smbcli_rename(struct smbcli_tree *tree, const char *fname_src,
+ const char *fname_dst)
+{
+ union smb_rename parms;
+
+ parms.generic.level = RAW_RENAME_RENAME;
+ parms.rename.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY;
+ parms.rename.in.pattern1 = fname_src;
+ parms.rename.in.pattern2 = fname_dst;
+
+ return smb_raw_rename(tree, &parms);
+}
+
+
+/****************************************************************************
+ Delete a file.
+****************************************************************************/
+NTSTATUS smbcli_unlink(struct smbcli_tree *tree, const char *fname)
+{
+ union smb_unlink parms;
+
+ parms.unlink.in.pattern = fname;
+ if (strchr(fname, '*')) {
+ parms.unlink.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ } else {
+ parms.unlink.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY;
+ }
+
+ return smb_raw_unlink(tree, &parms);
+}
+
+/****************************************************************************
+ Create a directory.
+****************************************************************************/
+NTSTATUS smbcli_mkdir(struct smbcli_tree *tree, const char *dname)
+{
+ union smb_mkdir parms;
+
+ parms.mkdir.level = RAW_MKDIR_MKDIR;
+ parms.mkdir.in.path = dname;
+
+ return smb_raw_mkdir(tree, &parms);
+}
+
+
+/****************************************************************************
+ Remove a directory.
+****************************************************************************/
+NTSTATUS smbcli_rmdir(struct smbcli_tree *tree, const char *dname)
+{
+ struct smb_rmdir parms;
+
+ parms.in.path = dname;
+
+ return smb_raw_rmdir(tree, &parms);
+}
+
+
+/****************************************************************************
+ Set or clear the delete on close flag.
+****************************************************************************/
+NTSTATUS smbcli_nt_delete_on_close(struct smbcli_tree *tree, int fnum,
+ bool flag)
+{
+ union smb_setfileinfo parms;
+ NTSTATUS status;
+
+ parms.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO;
+ parms.disposition_info.in.file.fnum = fnum;
+ parms.disposition_info.in.delete_on_close = flag;
+
+ status = smb_raw_setfileinfo(tree, &parms);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Create/open a file - exposing the full horror of the NT API :-).
+ Used in CIFS-on-CIFS NTVFS.
+****************************************************************************/
+int smbcli_nt_create_full(struct smbcli_tree *tree, const char *fname,
+ uint32_t CreatFlags, uint32_t DesiredAccess,
+ uint32_t FileAttributes, uint32_t ShareAccess,
+ uint32_t CreateDisposition, uint32_t CreateOptions,
+ uint8_t SecurityFlags)
+{
+ union smb_open open_parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("raw_open");
+ if (!mem_ctx) return -1;
+
+ open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
+ open_parms.ntcreatex.in.flags = CreatFlags;
+ open_parms.ntcreatex.in.root_fid = 0;
+ open_parms.ntcreatex.in.access_mask = DesiredAccess;
+ open_parms.ntcreatex.in.file_attr = FileAttributes;
+ open_parms.ntcreatex.in.alloc_size = 0;
+ open_parms.ntcreatex.in.share_access = ShareAccess;
+ open_parms.ntcreatex.in.open_disposition = CreateDisposition;
+ open_parms.ntcreatex.in.create_options = CreateOptions;
+ open_parms.ntcreatex.in.impersonation = 0;
+ open_parms.ntcreatex.in.security_flags = SecurityFlags;
+ open_parms.ntcreatex.in.fname = fname;
+
+ status = smb_raw_open(tree, mem_ctx, &open_parms);
+ talloc_free(mem_ctx);
+
+ if (NT_STATUS_IS_OK(status)) {
+ return open_parms.ntcreatex.out.file.fnum;
+ }
+
+ return -1;
+}
+
+
+/****************************************************************************
+ Open a file (using SMBopenx)
+ WARNING: if you open with O_WRONLY then getattrE won't work!
+****************************************************************************/
+int smbcli_open(struct smbcli_tree *tree, const char *fname, int flags,
+ int share_mode)
+{
+ union smb_open open_parms;
+ uint_t openfn=0;
+ uint_t accessmode=0;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("raw_open");
+ if (!mem_ctx) return -1;
+
+ if (flags & O_CREAT) {
+ openfn |= OPENX_OPEN_FUNC_CREATE;
+ }
+ if (!(flags & O_EXCL)) {
+ if (flags & O_TRUNC) {
+ openfn |= OPENX_OPEN_FUNC_TRUNC;
+ } else {
+ openfn |= OPENX_OPEN_FUNC_OPEN;
+ }
+ }
+
+ accessmode = (share_mode<<OPENX_MODE_DENY_SHIFT);
+
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ accessmode |= OPENX_MODE_ACCESS_RDWR;
+ } else if ((flags & O_ACCMODE) == O_WRONLY) {
+ accessmode |= OPENX_MODE_ACCESS_WRITE;
+ }
+
+#if defined(O_SYNC)
+ if ((flags & O_SYNC) == O_SYNC) {
+ accessmode |= OPENX_MODE_WRITE_THRU;
+ }
+#endif
+
+ if (share_mode == DENY_FCB) {
+ accessmode = OPENX_MODE_ACCESS_FCB | OPENX_MODE_DENY_FCB;
+ }
+
+ open_parms.openx.level = RAW_OPEN_OPENX;
+ open_parms.openx.in.flags = 0;
+ open_parms.openx.in.open_mode = accessmode;
+ open_parms.openx.in.search_attrs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ open_parms.openx.in.file_attrs = 0;
+ open_parms.openx.in.write_time = 0;
+ open_parms.openx.in.open_func = openfn;
+ open_parms.openx.in.size = 0;
+ open_parms.openx.in.timeout = 0;
+ open_parms.openx.in.fname = fname;
+
+ status = smb_raw_open(tree, mem_ctx, &open_parms);
+ talloc_free(mem_ctx);
+
+ if (NT_STATUS_IS_OK(status)) {
+ return open_parms.openx.out.file.fnum;
+ }
+
+ return -1;
+}
+
+
+/****************************************************************************
+ Close a file.
+****************************************************************************/
+NTSTATUS smbcli_close(struct smbcli_tree *tree, int fnum)
+{
+ union smb_close close_parms;
+ NTSTATUS status;
+
+ close_parms.close.level = RAW_CLOSE_CLOSE;
+ close_parms.close.in.file.fnum = fnum;
+ close_parms.close.in.write_time = 0;
+ status = smb_raw_close(tree, &close_parms);
+ return status;
+}
+
+/****************************************************************************
+ send a lock with a specified locktype
+ this is used for testing LOCKING_ANDX_CANCEL_LOCK
+****************************************************************************/
+NTSTATUS smbcli_locktype(struct smbcli_tree *tree, int fnum,
+ uint32_t offset, uint32_t len, int timeout,
+ uint8_t locktype)
+{
+ union smb_lock parms;
+ struct smb_lock_entry lock[1];
+ NTSTATUS status;
+
+ parms.lockx.level = RAW_LOCK_LOCKX;
+ parms.lockx.in.file.fnum = fnum;
+ parms.lockx.in.mode = locktype;
+ parms.lockx.in.timeout = timeout;
+ parms.lockx.in.ulock_cnt = 0;
+ parms.lockx.in.lock_cnt = 1;
+ lock[0].pid = tree->session->pid;
+ lock[0].offset = offset;
+ lock[0].count = len;
+ parms.lockx.in.locks = &lock[0];
+
+ status = smb_raw_lock(tree, &parms);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Lock a file.
+****************************************************************************/
+NTSTATUS smbcli_lock(struct smbcli_tree *tree, int fnum,
+ uint32_t offset, uint32_t len, int timeout,
+ enum brl_type lock_type)
+{
+ union smb_lock parms;
+ struct smb_lock_entry lock[1];
+ NTSTATUS status;
+
+ parms.lockx.level = RAW_LOCK_LOCKX;
+ parms.lockx.in.file.fnum = fnum;
+ parms.lockx.in.mode = (lock_type == READ_LOCK? 1 : 0);
+ parms.lockx.in.timeout = timeout;
+ parms.lockx.in.ulock_cnt = 0;
+ parms.lockx.in.lock_cnt = 1;
+ lock[0].pid = tree->session->pid;
+ lock[0].offset = offset;
+ lock[0].count = len;
+ parms.lockx.in.locks = &lock[0];
+
+ status = smb_raw_lock(tree, &parms);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Unlock a file.
+****************************************************************************/
+NTSTATUS smbcli_unlock(struct smbcli_tree *tree, int fnum, uint32_t offset, uint32_t len)
+{
+ union smb_lock parms;
+ struct smb_lock_entry lock[1];
+ NTSTATUS status;
+
+ parms.lockx.level = RAW_LOCK_LOCKX;
+ parms.lockx.in.file.fnum = fnum;
+ parms.lockx.in.mode = 0;
+ parms.lockx.in.timeout = 0;
+ parms.lockx.in.ulock_cnt = 1;
+ parms.lockx.in.lock_cnt = 0;
+ lock[0].pid = tree->session->pid;
+ lock[0].offset = offset;
+ lock[0].count = len;
+ parms.lockx.in.locks = &lock[0];
+
+ status = smb_raw_lock(tree, &parms);
+ return status;
+}
+
+
+/****************************************************************************
+ Lock a file with 64 bit offsets.
+****************************************************************************/
+NTSTATUS smbcli_lock64(struct smbcli_tree *tree, int fnum,
+ off_t offset, off_t len, int timeout,
+ enum brl_type lock_type)
+{
+ union smb_lock parms;
+ int ltype;
+ struct smb_lock_entry lock[1];
+ NTSTATUS status;
+
+ if (!(tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+ return smbcli_lock(tree, fnum, offset, len, timeout, lock_type);
+ }
+
+ parms.lockx.level = RAW_LOCK_LOCKX;
+ parms.lockx.in.file.fnum = fnum;
+
+ ltype = (lock_type == READ_LOCK? 1 : 0);
+ ltype |= LOCKING_ANDX_LARGE_FILES;
+ parms.lockx.in.mode = ltype;
+ parms.lockx.in.timeout = timeout;
+ parms.lockx.in.ulock_cnt = 0;
+ parms.lockx.in.lock_cnt = 1;
+ lock[0].pid = tree->session->pid;
+ lock[0].offset = offset;
+ lock[0].count = len;
+ parms.lockx.in.locks = &lock[0];
+
+ status = smb_raw_lock(tree, &parms);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Unlock a file with 64 bit offsets.
+****************************************************************************/
+NTSTATUS smbcli_unlock64(struct smbcli_tree *tree, int fnum, off_t offset,
+ off_t len)
+{
+ union smb_lock parms;
+ struct smb_lock_entry lock[1];
+ NTSTATUS status;
+
+ if (!(tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+ return smbcli_unlock(tree, fnum, offset, len);
+ }
+
+ parms.lockx.level = RAW_LOCK_LOCKX;
+ parms.lockx.in.file.fnum = fnum;
+ parms.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+ parms.lockx.in.timeout = 0;
+ parms.lockx.in.ulock_cnt = 1;
+ parms.lockx.in.lock_cnt = 0;
+ lock[0].pid = tree->session->pid;
+ lock[0].offset = offset;
+ lock[0].count = len;
+ parms.lockx.in.locks = &lock[0];
+
+ status = smb_raw_lock(tree, &parms);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Do a SMBgetattrE call.
+****************************************************************************/
+NTSTATUS smbcli_getattrE(struct smbcli_tree *tree, int fnum,
+ uint16_t *attr, size_t *size,
+ time_t *c_time, time_t *a_time, time_t *m_time)
+{
+ union smb_fileinfo parms;
+ NTSTATUS status;
+
+ parms.getattre.level = RAW_FILEINFO_GETATTRE;
+ parms.getattre.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(tree, NULL, &parms);
+
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ if (size) {
+ *size = parms.getattre.out.size;
+ }
+
+ if (attr) {
+ *attr = parms.getattre.out.attrib;
+ }
+
+ if (c_time) {
+ *c_time = parms.getattre.out.create_time;
+ }
+
+ if (a_time) {
+ *a_time = parms.getattre.out.access_time;
+ }
+
+ if (m_time) {
+ *m_time = parms.getattre.out.write_time;
+ }
+
+ return status;
+}
+
+/****************************************************************************
+ Do a SMBgetatr call
+****************************************************************************/
+NTSTATUS smbcli_getatr(struct smbcli_tree *tree, const char *fname,
+ uint16_t *attr, size_t *size, time_t *t)
+{
+ union smb_fileinfo parms;
+ NTSTATUS status;
+
+ parms.getattr.level = RAW_FILEINFO_GETATTR;
+ parms.getattr.in.file.path = fname;
+
+ status = smb_raw_pathinfo(tree, NULL, &parms);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (size) {
+ *size = parms.getattr.out.size;
+ }
+
+ if (t) {
+ *t = parms.getattr.out.write_time;
+ }
+
+ if (attr) {
+ *attr = parms.getattr.out.attrib;
+ }
+
+ return status;
+}
+
+
+/****************************************************************************
+ Do a SMBsetatr call.
+****************************************************************************/
+NTSTATUS smbcli_setatr(struct smbcli_tree *tree, const char *fname, uint16_t mode,
+ time_t t)
+{
+ union smb_setfileinfo parms;
+
+ parms.setattr.level = RAW_SFILEINFO_SETATTR;
+ parms.setattr.in.file.path = fname;
+ parms.setattr.in.attrib = mode;
+ parms.setattr.in.write_time = t;
+
+ return smb_raw_setpathinfo(tree, &parms);
+}
+
+/****************************************************************************
+ Do a setfileinfo basic_info call.
+****************************************************************************/
+NTSTATUS smbcli_fsetatr(struct smbcli_tree *tree, int fnum, uint16_t mode,
+ NTTIME create_time, NTTIME access_time,
+ NTTIME write_time, NTTIME change_time)
+{
+ union smb_setfileinfo parms;
+
+ parms.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
+ parms.basic_info.in.file.fnum = fnum;
+ parms.basic_info.in.attrib = mode;
+ parms.basic_info.in.create_time = create_time;
+ parms.basic_info.in.access_time = access_time;
+ parms.basic_info.in.write_time = write_time;
+ parms.basic_info.in.change_time = change_time;
+
+ return smb_raw_setfileinfo(tree, &parms);
+}
+
+
+/****************************************************************************
+ truncate a file to a given size
+****************************************************************************/
+NTSTATUS smbcli_ftruncate(struct smbcli_tree *tree, int fnum, uint64_t size)
+{
+ union smb_setfileinfo parms;
+
+ parms.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFO;
+ parms.end_of_file_info.in.file.fnum = fnum;
+ parms.end_of_file_info.in.size = size;
+
+ return smb_raw_setfileinfo(tree, &parms);
+}
+
+
+/****************************************************************************
+ Check for existence of a dir.
+****************************************************************************/
+NTSTATUS smbcli_chkpath(struct smbcli_tree *tree, const char *path)
+{
+ union smb_chkpath parms;
+ char *path2;
+ NTSTATUS status;
+
+ path2 = strdup(path);
+ trim_string(path2,NULL,"\\");
+ if (!*path2) {
+ free(path2);
+ path2 = strdup("\\");
+ }
+
+ parms.chkpath.in.path = path2;
+
+ status = smb_raw_chkpath(tree, &parms);
+
+ free(path2);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Query disk space.
+****************************************************************************/
+NTSTATUS smbcli_dskattr(struct smbcli_tree *tree, uint32_t *bsize,
+ uint64_t *total, uint64_t *avail)
+{
+ union smb_fsinfo fsinfo_parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("smbcli_dskattr");
+
+ fsinfo_parms.dskattr.level = RAW_QFS_SIZE_INFO;
+ status = smb_raw_fsinfo(tree, mem_ctx, &fsinfo_parms);
+ if (NT_STATUS_IS_OK(status)) {
+ *bsize = fsinfo_parms.size_info.out.bytes_per_sector * fsinfo_parms.size_info.out.sectors_per_unit;
+ *total = fsinfo_parms.size_info.out.total_alloc_units;
+ *avail = fsinfo_parms.size_info.out.avail_alloc_units;
+ }
+
+ talloc_free(mem_ctx);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Create and open a temporary file.
+****************************************************************************/
+int smbcli_ctemp(struct smbcli_tree *tree, const char *path, char **tmp_path)
+{
+ union smb_open open_parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("raw_open");
+ if (!mem_ctx) return -1;
+
+ open_parms.openx.level = RAW_OPEN_CTEMP;
+ open_parms.ctemp.in.attrib = 0;
+ open_parms.ctemp.in.directory = path;
+ open_parms.ctemp.in.write_time = 0;
+
+ status = smb_raw_open(tree, mem_ctx, &open_parms);
+ if (tmp_path) {
+ *tmp_path = strdup(open_parms.ctemp.out.name);
+ }
+ talloc_free(mem_ctx);
+ if (NT_STATUS_IS_OK(status)) {
+ return open_parms.ctemp.out.file.fnum;
+ }
+ return -1;
+}
diff --git a/source4/libcli/clilist.c b/source4/libcli/clilist.c
new file mode 100644
index 0000000000..65ee0a8303
--- /dev/null
+++ b/source4/libcli/clilist.c
@@ -0,0 +1,356 @@
+/*
+ Unix SMB/CIFS implementation.
+ client directory list routines
+ Copyright (C) Andrew Tridgell 1994-2003
+ Copyright (C) James Myers 2003 <myersjj@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 "includes.h"
+#include "libcli/libcli.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+struct search_private {
+ struct clilist_file_info *dirlist;
+ TALLOC_CTX *mem_ctx;
+ int dirlist_len;
+ int ff_searchcount; /* total received in 1 server trip */
+ int total_received; /* total received all together */
+ enum smb_search_data_level data_level;
+ const char *last_name; /* used to continue trans2 search */
+ struct smb_search_id id; /* used for old-style search */
+};
+
+
+/****************************************************************************
+ Interpret a long filename structure.
+****************************************************************************/
+static bool interpret_long_filename(enum smb_search_data_level level,
+ const union smb_search_data *info,
+ struct clilist_file_info *finfo)
+{
+ struct clilist_file_info finfo2;
+
+ if (!finfo) finfo = &finfo2;
+ ZERO_STRUCTP(finfo);
+
+ switch (level) {
+ case RAW_SEARCH_DATA_STANDARD:
+ finfo->size = info->standard.size;
+ finfo->mtime = info->standard.write_time;
+ finfo->attrib = info->standard.attrib;
+ finfo->name = info->standard.name.s;
+ finfo->short_name = info->standard.name.s;
+ break;
+
+ case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
+ finfo->size = info->both_directory_info.size;
+ finfo->mtime = nt_time_to_unix(info->both_directory_info.write_time);
+ finfo->attrib = info->both_directory_info.attrib;
+ finfo->short_name = info->both_directory_info.short_name.s;
+ finfo->name = info->both_directory_info.name.s;
+ break;
+
+ default:
+ DEBUG(0,("Unhandled level %d in interpret_long_filename\n", (int)level));
+ return false;
+ }
+
+ return true;
+}
+
+/* callback function used for trans2 search */
+static bool smbcli_list_new_callback(void *private_data, const union smb_search_data *file)
+{
+ struct search_private *state = (struct search_private*) private_data;
+ struct clilist_file_info *tdl;
+
+ /* add file info to the dirlist pool */
+ tdl = talloc_realloc(state,
+ state->dirlist,
+ struct clilist_file_info,
+ state->dirlist_len + 1);
+ if (!tdl) {
+ return false;
+ }
+ state->dirlist = tdl;
+ state->dirlist_len++;
+
+ interpret_long_filename(state->data_level, file, &state->dirlist[state->total_received]);
+
+ state->last_name = state->dirlist[state->total_received].name;
+ state->total_received++;
+ state->ff_searchcount++;
+
+ return true;
+}
+
+int smbcli_list_new(struct smbcli_tree *tree, const char *Mask, uint16_t attribute,
+ enum smb_search_data_level level,
+ void (*fn)(struct clilist_file_info *, const char *, void *),
+ void *caller_state)
+{
+ union smb_search_first first_parms;
+ union smb_search_next next_parms;
+ struct search_private state; /* for callbacks */
+ int received = 0;
+ bool first = true;
+ int num_received = 0;
+ int max_matches = 512;
+ char *mask;
+ int ff_eos = 0, i, ff_searchcount;
+ int ff_dir_handle=0;
+
+ /* initialize state for search */
+ state.mem_ctx = talloc_init("smbcli_list_new");
+ state.dirlist_len = 0;
+ state.total_received = 0;
+
+ state.dirlist = talloc_array(state.mem_ctx,
+ struct clilist_file_info, 0);
+ mask = talloc_strdup(state.mem_ctx, Mask);
+
+ if (level == RAW_SEARCH_DATA_GENERIC) {
+ if (tree->session->transport->negotiate.capabilities & CAP_NT_SMBS) {
+ level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
+ } else {
+ level = RAW_SEARCH_DATA_STANDARD;
+ }
+ }
+ state.data_level = level;
+
+ while (1) {
+ state.ff_searchcount = 0;
+ if (first) {
+ NTSTATUS status;
+
+ first_parms.t2ffirst.level = RAW_SEARCH_TRANS2;
+ first_parms.t2ffirst.data_level = state.data_level;
+ first_parms.t2ffirst.in.max_count = max_matches;
+ first_parms.t2ffirst.in.search_attrib = attribute;
+ first_parms.t2ffirst.in.pattern = mask;
+ first_parms.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
+ first_parms.t2ffirst.in.storage_type = 0;
+
+ status = smb_raw_search_first(tree,
+ state.mem_ctx, &first_parms,
+ (void*)&state, smbcli_list_new_callback);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(state.mem_ctx);
+ return -1;
+ }
+
+ ff_dir_handle = first_parms.t2ffirst.out.handle;
+ ff_searchcount = first_parms.t2ffirst.out.count;
+ ff_eos = first_parms.t2ffirst.out.end_of_search;
+
+ received = first_parms.t2ffirst.out.count;
+ if (received <= 0) break;
+ if (ff_eos) break;
+ first = false;
+ } else {
+ NTSTATUS status;
+
+ next_parms.t2fnext.level = RAW_SEARCH_TRANS2;
+ next_parms.t2fnext.data_level = state.data_level;
+ next_parms.t2fnext.in.max_count = max_matches;
+ next_parms.t2fnext.in.last_name = state.last_name;
+ next_parms.t2fnext.in.handle = ff_dir_handle;
+ next_parms.t2fnext.in.resume_key = 0;
+ next_parms.t2fnext.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
+
+ status = smb_raw_search_next(tree,
+ state.mem_ctx,
+ &next_parms,
+ (void*)&state,
+ smbcli_list_new_callback);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+ ff_searchcount = next_parms.t2fnext.out.count;
+ ff_eos = next_parms.t2fnext.out.end_of_search;
+ received = next_parms.t2fnext.out.count;
+ if (received <= 0) break;
+ if (ff_eos) break;
+ }
+
+ num_received += received;
+ }
+
+ for (i=0;i<state.total_received;i++) {
+ fn(&state.dirlist[i], Mask, caller_state);
+ }
+
+ talloc_free(state.mem_ctx);
+
+ return state.total_received;
+}
+
+/****************************************************************************
+ Interpret a short filename structure.
+ The length of the structure is returned.
+****************************************************************************/
+static bool interpret_short_filename(enum smb_search_data_level level,
+ const union smb_search_data *info,
+ struct clilist_file_info *finfo)
+{
+ struct clilist_file_info finfo2;
+
+ if (!finfo) finfo = &finfo2;
+ ZERO_STRUCTP(finfo);
+
+ switch (level) {
+ case RAW_SEARCH_DATA_SEARCH:
+ finfo->mtime = info->search.write_time;
+ finfo->size = info->search.size;
+ finfo->attrib = info->search.attrib;
+ finfo->name = info->search.name;
+ finfo->short_name = info->search.name;
+ break;
+
+ default:
+ DEBUG(0,("Unhandled level %d in interpret_short_filename\n", (int)level));
+ return false;
+ }
+
+ return true;
+}
+
+/* callback function used for smb_search */
+static bool smbcli_list_old_callback(void *private_data, const union smb_search_data *file)
+{
+ struct search_private *state = (struct search_private*) private_data;
+ struct clilist_file_info *tdl;
+
+ /* add file info to the dirlist pool */
+ tdl = talloc_realloc(state,
+ state->dirlist,
+ struct clilist_file_info,
+ state->dirlist_len + 1);
+
+ if (!tdl) {
+ return false;
+ }
+ state->dirlist = tdl;
+ state->dirlist_len++;
+
+ interpret_short_filename(state->data_level, file, &state->dirlist[state->total_received]);
+
+ state->total_received++;
+ state->ff_searchcount++;
+ state->id = file->search.id; /* return resume info */
+
+ return true;
+}
+
+int smbcli_list_old(struct smbcli_tree *tree, const char *Mask, uint16_t attribute,
+ void (*fn)(struct clilist_file_info *, const char *, void *),
+ void *caller_state)
+{
+ union smb_search_first first_parms;
+ union smb_search_next next_parms;
+ struct search_private state; /* for callbacks */
+ const int num_asked = 500;
+ int received = 0;
+ bool first = true;
+ int num_received = 0;
+ char *mask;
+ int i;
+
+ /* initialize state for search */
+ state.mem_ctx = talloc_init("smbcli_list_old");
+ state.dirlist_len = 0;
+ state.total_received = 0;
+ state.data_level = RAW_SEARCH_DATA_SEARCH;
+
+ state.dirlist = talloc_array(state.mem_ctx, struct clilist_file_info,
+ 0);
+ mask = talloc_strdup(state.mem_ctx, Mask);
+
+ while (1) {
+ state.ff_searchcount = 0;
+ if (first) {
+ NTSTATUS status;
+
+ first_parms.search_first.level = RAW_SEARCH_SEARCH;
+ first_parms.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
+ first_parms.search_first.in.max_count = num_asked;
+ first_parms.search_first.in.search_attrib = attribute;
+ first_parms.search_first.in.pattern = mask;
+
+ status = smb_raw_search_first(tree, state.mem_ctx,
+ &first_parms,
+ (void*)&state,
+ smbcli_list_old_callback);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(state.mem_ctx);
+ return -1;
+ }
+
+ received = first_parms.search_first.out.count;
+ if (received <= 0) break;
+ first = false;
+ } else {
+ NTSTATUS status;
+
+ next_parms.search_next.level = RAW_SEARCH_SEARCH;
+ next_parms.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
+ next_parms.search_next.in.max_count = num_asked;
+ next_parms.search_next.in.search_attrib = attribute;
+ next_parms.search_next.in.id = state.id;
+
+ status = smb_raw_search_next(tree, state.mem_ctx,
+ &next_parms,
+ (void*)&state,
+ smbcli_list_old_callback);
+
+ if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
+ break;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(state.mem_ctx);
+ return -1;
+ }
+ received = next_parms.search_next.out.count;
+ if (received <= 0) break;
+ }
+
+ num_received += received;
+ }
+
+ for (i=0;i<state.total_received;i++) {
+ fn(&state.dirlist[i], Mask, caller_state);
+ }
+
+ talloc_free(state.mem_ctx);
+
+ return state.total_received;
+}
+
+/****************************************************************************
+ Do a directory listing, calling fn on each file found.
+ This auto-switches between old and new style.
+****************************************************************************/
+
+int smbcli_list(struct smbcli_tree *tree, const char *Mask,uint16_t attribute,
+ void (*fn)(struct clilist_file_info *, const char *, void *), void *state)
+{
+ if (tree->session->transport->negotiate.protocol <= PROTOCOL_LANMAN1)
+ return smbcli_list_old(tree, Mask, attribute, fn, state);
+ return smbcli_list_new(tree, Mask, attribute, RAW_SEARCH_DATA_GENERIC, fn, state);
+}
diff --git a/source4/libcli/climessage.c b/source4/libcli/climessage.c
new file mode 100644
index 0000000000..5ed0e8e3cd
--- /dev/null
+++ b/source4/libcli/climessage.c
@@ -0,0 +1,95 @@
+/*
+ Unix SMB/CIFS implementation.
+ client message handling routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) James J Myers 2003 <myersjj@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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+
+
+/****************************************************************************
+start a message sequence
+****************************************************************************/
+bool smbcli_message_start(struct smbcli_tree *tree, const char *host, const char *username,
+ int *grp)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBsendstrt, 0, 0);
+ smbcli_req_append_string(req, username, STR_TERMINATE);
+ smbcli_req_append_string(req, host, STR_TERMINATE);
+ if (!smbcli_request_send(req) ||
+ !smbcli_request_receive(req) ||
+ smbcli_is_error(tree)) {
+ smbcli_request_destroy(req);
+ return false;
+ }
+
+ *grp = SVAL(req->in.vwv, VWV(0));
+ smbcli_request_destroy(req);
+
+ return true;
+}
+
+
+/****************************************************************************
+send a message
+****************************************************************************/
+bool smbcli_message_text(struct smbcli_tree *tree, char *msg, int len, int grp)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBsendtxt, 1, 0);
+ SSVAL(req->out.vwv, VWV(0), grp);
+
+ smbcli_req_append_bytes(req, (const uint8_t *)msg, len);
+
+ if (!smbcli_request_send(req) ||
+ !smbcli_request_receive(req) ||
+ smbcli_is_error(tree)) {
+ smbcli_request_destroy(req);
+ return false;
+ }
+
+ smbcli_request_destroy(req);
+ return true;
+}
+
+/****************************************************************************
+end a message
+****************************************************************************/
+bool smbcli_message_end(struct smbcli_tree *tree, int grp)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBsendend, 1, 0);
+ SSVAL(req->out.vwv, VWV(0), grp);
+
+ if (!smbcli_request_send(req) ||
+ !smbcli_request_receive(req) ||
+ smbcli_is_error(tree)) {
+ smbcli_request_destroy(req);
+ return false;
+ }
+
+ smbcli_request_destroy(req);
+ return true;
+}
+
diff --git a/source4/libcli/clireadwrite.c b/source4/libcli/clireadwrite.c
new file mode 100644
index 0000000000..ae2367918c
--- /dev/null
+++ b/source4/libcli/clireadwrite.c
@@ -0,0 +1,167 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file read/write routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) James Myers 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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+
+/****************************************************************************
+ Read size bytes at offset offset using SMBreadX.
+****************************************************************************/
+ssize_t smbcli_read(struct smbcli_tree *tree, int fnum, void *_buf, off_t offset,
+ size_t size)
+{
+ uint8_t *buf = (uint8_t *)_buf;
+ union smb_read parms;
+ int readsize;
+ ssize_t total = 0;
+
+ if (size == 0) {
+ return 0;
+ }
+
+ parms.readx.level = RAW_READ_READX;
+ parms.readx.in.file.fnum = fnum;
+
+ /*
+ * Set readsize to the maximum size we can handle in one readX,
+ * rounded down to a multiple of 1024.
+ */
+ readsize = (tree->session->transport->negotiate.max_xmit - (MIN_SMB_SIZE+32));
+ if (readsize > 0xFFFF) readsize = 0xFFFF;
+
+ while (total < size) {
+ NTSTATUS status;
+
+ readsize = MIN(readsize, size-total);
+
+ parms.readx.in.offset = offset;
+ parms.readx.in.mincnt = readsize;
+ parms.readx.in.maxcnt = readsize;
+ parms.readx.in.remaining = size - total;
+ parms.readx.in.read_for_execute = false;
+ parms.readx.out.data = buf + total;
+
+ status = smb_raw_read(tree, &parms);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ total += parms.readx.out.nread;
+ offset += parms.readx.out.nread;
+
+ /* If the server returned less than we asked for we're at EOF */
+ if (parms.readx.out.nread < readsize)
+ break;
+ }
+
+ return total;
+}
+
+
+/****************************************************************************
+ write to a file
+ write_mode: 0x0001 disallow write cacheing
+ 0x0002 return bytes remaining
+ 0x0004 use raw named pipe protocol
+ 0x0008 start of message mode named pipe protocol
+****************************************************************************/
+ssize_t smbcli_write(struct smbcli_tree *tree,
+ int fnum, uint16_t write_mode,
+ const void *_buf, off_t offset, size_t size)
+{
+ const uint8_t *buf = (const uint8_t *)_buf;
+ union smb_write parms;
+ int block = (tree->session->transport->negotiate.max_xmit - (MIN_SMB_SIZE+32));
+ ssize_t total = 0;
+
+ if (size == 0) {
+ return 0;
+ }
+
+ if (block > 0xFFFF) block = 0xFFFF;
+
+
+ parms.writex.level = RAW_WRITE_WRITEX;
+ parms.writex.in.file.fnum = fnum;
+ parms.writex.in.wmode = write_mode;
+ parms.writex.in.remaining = 0;
+
+ while (total < size) {
+ NTSTATUS status;
+
+ block = MIN(block, size - total);
+
+ parms.writex.in.offset = offset;
+ parms.writex.in.count = block;
+ parms.writex.in.data = buf;
+
+ status = smb_raw_write(tree, &parms);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ offset += parms.writex.out.nwritten;
+ total += parms.writex.out.nwritten;
+ buf += parms.writex.out.nwritten;
+ }
+
+ return total;
+}
+
+/****************************************************************************
+ write to a file using a SMBwrite and not bypassing 0 byte writes
+****************************************************************************/
+ssize_t smbcli_smbwrite(struct smbcli_tree *tree,
+ int fnum, const void *_buf, off_t offset, size_t size1)
+{
+ const uint8_t *buf = (const uint8_t *)_buf;
+ union smb_write parms;
+ ssize_t total = 0;
+
+ parms.write.level = RAW_WRITE_WRITE;
+ parms.write.in.remaining = 0;
+
+ do {
+ size_t size = MIN(size1, tree->session->transport->negotiate.max_xmit - 48);
+ if (size > 0xFFFF) size = 0xFFFF;
+
+ parms.write.in.file.fnum = fnum;
+ parms.write.in.offset = offset;
+ parms.write.in.count = size;
+ parms.write.in.data = buf + total;
+
+ if (NT_STATUS_IS_ERR(smb_raw_write(tree, &parms)))
+ return -1;
+
+ size = parms.write.out.nwritten;
+ if (size == 0)
+ break;
+
+ size1 -= size;
+ total += size;
+ offset += size;
+ } while (size1);
+
+ return total;
+}
diff --git a/source4/libcli/clitrans2.c b/source4/libcli/clitrans2.c
new file mode 100644
index 0000000000..5c5ba6585f
--- /dev/null
+++ b/source4/libcli/clitrans2.c
@@ -0,0 +1,224 @@
+/*
+ Unix SMB/CIFS implementation.
+ client trans2 calls
+ Copyright (C) James J Myers 2003 <myersjj@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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+
+/****************************************************************************
+send a qpathinfo call
+****************************************************************************/
+NTSTATUS smbcli_qpathinfo(struct smbcli_tree *tree, const char *fname,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ size_t *size, uint16_t *mode)
+{
+ union smb_fileinfo parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("smbcli_qpathinfo");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ parms.standard.level = RAW_FILEINFO_STANDARD;
+ parms.standard.in.file.path = fname;
+
+ status = smb_raw_pathinfo(tree, mem_ctx, &parms);
+ talloc_free(mem_ctx);
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ if (c_time) {
+ *c_time = parms.standard.out.create_time;
+ }
+ if (a_time) {
+ *a_time = parms.standard.out.access_time;
+ }
+ if (m_time) {
+ *m_time = parms.standard.out.write_time;
+ }
+ if (size) {
+ *size = parms.standard.out.size;
+ }
+ if (mode) {
+ *mode = parms.standard.out.attrib;
+ }
+
+ return status;
+}
+
+/****************************************************************************
+send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level
+****************************************************************************/
+NTSTATUS smbcli_qpathinfo2(struct smbcli_tree *tree, const char *fname,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ time_t *w_time, size_t *size, uint16_t *mode,
+ ino_t *ino)
+{
+ union smb_fileinfo parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("smbcli_qfilename");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ parms.all_info.level = RAW_FILEINFO_ALL_INFO;
+ parms.all_info.in.file.path = fname;
+
+ status = smb_raw_pathinfo(tree, mem_ctx, &parms);
+ talloc_free(mem_ctx);
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ if (c_time) {
+ *c_time = nt_time_to_unix(parms.all_info.out.create_time);
+ }
+ if (a_time) {
+ *a_time = nt_time_to_unix(parms.all_info.out.access_time);
+ }
+ if (m_time) {
+ *m_time = nt_time_to_unix(parms.all_info.out.change_time);
+ }
+ if (w_time) {
+ *w_time = nt_time_to_unix(parms.all_info.out.write_time);
+ }
+ if (size) {
+ *size = parms.all_info.out.size;
+ }
+ if (mode) {
+ *mode = parms.all_info.out.attrib;
+ }
+
+ return status;
+}
+
+
+/****************************************************************************
+send a qfileinfo QUERY_FILE_NAME_INFO call
+****************************************************************************/
+NTSTATUS smbcli_qfilename(struct smbcli_tree *tree, int fnum, const char **name)
+{
+ union smb_fileinfo parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("smbcli_qfilename");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ parms.name_info.level = RAW_FILEINFO_NAME_INFO;
+ parms.name_info.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(tree, mem_ctx, &parms);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ *name = NULL;
+ return status;
+ }
+
+ *name = strdup(parms.name_info.out.fname.s);
+
+ talloc_free(mem_ctx);
+
+ return status;
+}
+
+
+/****************************************************************************
+send a qfileinfo call
+****************************************************************************/
+NTSTATUS smbcli_qfileinfo(struct smbcli_tree *tree, int fnum,
+ uint16_t *mode, size_t *size,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ time_t *w_time, ino_t *ino)
+{
+ union smb_fileinfo parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("smbcli_qfileinfo");
+ if (!mem_ctx)
+ return NT_STATUS_NO_MEMORY;
+
+ parms.all_info.level = RAW_FILEINFO_ALL_INFO;
+ parms.all_info.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(tree, mem_ctx, &parms);
+ talloc_free(mem_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (c_time) {
+ *c_time = nt_time_to_unix(parms.all_info.out.create_time);
+ }
+ if (a_time) {
+ *a_time = nt_time_to_unix(parms.all_info.out.access_time);
+ }
+ if (m_time) {
+ *m_time = nt_time_to_unix(parms.all_info.out.change_time);
+ }
+ if (w_time) {
+ *w_time = nt_time_to_unix(parms.all_info.out.write_time);
+ }
+ if (mode) {
+ *mode = parms.all_info.out.attrib;
+ }
+ if (size) {
+ *size = (size_t)parms.all_info.out.size;
+ }
+ if (ino) {
+ *ino = 0;
+ }
+
+ return status;
+}
+
+
+/****************************************************************************
+send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call
+****************************************************************************/
+NTSTATUS smbcli_qpathinfo_alt_name(struct smbcli_tree *tree, const char *fname,
+ const char **alt_name)
+{
+ union smb_fileinfo parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ parms.alt_name_info.level = RAW_FILEINFO_ALT_NAME_INFO;
+ parms.alt_name_info.in.file.path = fname;
+
+ mem_ctx = talloc_init("smbcli_qpathinfo_alt_name");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ status = smb_raw_pathinfo(tree, mem_ctx, &parms);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ *alt_name = NULL;
+ return smbcli_nt_error(tree);
+ }
+
+ if (!parms.alt_name_info.out.fname.s) {
+ *alt_name = strdup("");
+ } else {
+ *alt_name = strdup(parms.alt_name_info.out.fname.s);
+ }
+
+ talloc_free(mem_ctx);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/composite/composite.c b/source4/libcli/composite/composite.c
new file mode 100644
index 0000000000..ab32175d00
--- /dev/null
+++ b/source4/libcli/composite/composite.c
@@ -0,0 +1,218 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Volker Lendecke 2005
+ Copyright (C) Andrew Tridgell 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/>.
+*/
+/*
+ composite API helper functions
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/composite/composite.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/rpc/dcerpc.h"
+#include "../libcli/nbt/libnbt.h"
+
+/*
+ create a new composite_context structure
+ and initialize it
+*/
+_PUBLIC_ struct composite_context *composite_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev)
+{
+ struct composite_context *c;
+
+ c = talloc_zero(mem_ctx, struct composite_context);
+ if (!c) return NULL;
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = talloc_reference(c, ev);
+ if (!c->event_ctx) {
+ talloc_free(c);
+ return NULL;
+ }
+
+ return c;
+}
+
+/*
+ block until a composite function has completed, then return the status
+*/
+_PUBLIC_ NTSTATUS composite_wait(struct composite_context *c)
+{
+ if (c == NULL) return NT_STATUS_NO_MEMORY;
+
+ c->used_wait = true;
+
+ while (c->state < COMPOSITE_STATE_DONE) {
+ if (event_loop_once(c->event_ctx) != 0) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ return c->status;
+}
+
+/*
+ block until a composite function has completed, then return the status.
+ Free the composite context before returning
+*/
+_PUBLIC_ NTSTATUS composite_wait_free(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+/*
+ callback from composite_done() and composite_error()
+
+ this is used to allow for a composite function to complete without
+ going through any state transitions. When that happens the caller
+ has had no opportunity to fill in the async callback fields
+ (ctx->async.fn and ctx->async.private_data) which means the usual way of
+ dealing with composite functions doesn't work. To cope with this,
+ we trigger a timer event that will happen then the event loop is
+ re-entered. This gives the caller a chance to setup the callback,
+ and allows the caller to ignore the fact that the composite
+ function completed early
+*/
+static void composite_trigger(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct composite_context *c = talloc_get_type(ptr, struct composite_context);
+ if (c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+
+_PUBLIC_ void composite_error(struct composite_context *ctx, NTSTATUS status)
+{
+ /* you are allowed to pass NT_STATUS_OK to composite_error(), in which
+ case it is equivalent to composite_done() */
+ if (NT_STATUS_IS_OK(status)) {
+ composite_done(ctx);
+ return;
+ }
+ if (!ctx->used_wait && !ctx->async.fn) {
+ event_add_timed(ctx->event_ctx, ctx, timeval_zero(), composite_trigger, ctx);
+ }
+ ctx->status = status;
+ ctx->state = COMPOSITE_STATE_ERROR;
+ if (ctx->async.fn != NULL) {
+ ctx->async.fn(ctx);
+ }
+}
+
+_PUBLIC_ bool composite_nomem(const void *p, struct composite_context *ctx)
+{
+ if (p != NULL) {
+ return false;
+ }
+ composite_error(ctx, NT_STATUS_NO_MEMORY);
+ return true;
+}
+
+_PUBLIC_ bool composite_is_ok(struct composite_context *ctx)
+{
+ if (NT_STATUS_IS_OK(ctx->status)) {
+ return true;
+ }
+ composite_error(ctx, ctx->status);
+ return false;
+}
+
+_PUBLIC_ void composite_done(struct composite_context *ctx)
+{
+ if (!ctx->used_wait && !ctx->async.fn) {
+ event_add_timed(ctx->event_ctx, ctx, timeval_zero(), composite_trigger, ctx);
+ }
+ ctx->state = COMPOSITE_STATE_DONE;
+ if (ctx->async.fn != NULL) {
+ ctx->async.fn(ctx);
+ }
+}
+
+_PUBLIC_ void composite_continue(struct composite_context *ctx,
+ struct composite_context *new_ctx,
+ void (*continuation)(struct composite_context *),
+ void *private_data)
+{
+ if (composite_nomem(new_ctx, ctx)) return;
+ new_ctx->async.fn = continuation;
+ new_ctx->async.private_data = private_data;
+
+ /* if we are setting up a continuation, and the context has
+ already finished, then we should run the callback with an
+ immediate event, otherwise we can be stuck forever */
+ if (new_ctx->state >= COMPOSITE_STATE_DONE && continuation) {
+ event_add_timed(new_ctx->event_ctx, new_ctx, timeval_zero(), composite_trigger, new_ctx);
+ }
+}
+
+_PUBLIC_ void composite_continue_rpc(struct composite_context *ctx,
+ struct rpc_request *new_req,
+ void (*continuation)(struct rpc_request *),
+ void *private_data)
+{
+ if (composite_nomem(new_req, ctx)) return;
+ new_req->async.callback = continuation;
+ new_req->async.private_data = private_data;
+}
+
+_PUBLIC_ void composite_continue_irpc(struct composite_context *ctx,
+ struct irpc_request *new_req,
+ void (*continuation)(struct irpc_request *),
+ void *private_data)
+{
+ if (composite_nomem(new_req, ctx)) return;
+ new_req->async.fn = continuation;
+ new_req->async.private_data = private_data;
+}
+
+_PUBLIC_ void composite_continue_smb(struct composite_context *ctx,
+ struct smbcli_request *new_req,
+ void (*continuation)(struct smbcli_request *),
+ void *private_data)
+{
+ if (composite_nomem(new_req, ctx)) return;
+ new_req->async.fn = continuation;
+ new_req->async.private_data = private_data;
+}
+
+_PUBLIC_ void composite_continue_smb2(struct composite_context *ctx,
+ struct smb2_request *new_req,
+ void (*continuation)(struct smb2_request *),
+ void *private_data)
+{
+ if (composite_nomem(new_req, ctx)) return;
+ new_req->async.fn = continuation;
+ new_req->async.private_data = private_data;
+}
+
+_PUBLIC_ void composite_continue_nbt(struct composite_context *ctx,
+ struct nbt_name_request *new_req,
+ void (*continuation)(struct nbt_name_request *),
+ void *private_data)
+{
+ if (composite_nomem(new_req, ctx)) return;
+ new_req->async.fn = continuation;
+ new_req->async.private_data = private_data;
+}
diff --git a/source4/libcli/composite/composite.h b/source4/libcli/composite/composite.h
new file mode 100644
index 0000000000..a99a762c5f
--- /dev/null
+++ b/source4/libcli/composite/composite.h
@@ -0,0 +1,109 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ composite request interfaces
+
+ Copyright (C) Andrew Tridgell 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 __COMPOSITE_H__
+#define __COMPOSITE_H__
+
+#include "libcli/raw/interfaces.h"
+
+struct tevent_context;
+
+/*
+ this defines the structures associated with "composite"
+ requests. Composite requests are libcli requests that are internally
+ implemented as multiple async calls, but can be treated as a
+ single call via these composite calls. The composite calls are
+ particularly designed to be used in async applications.
+ you can also stack multiple level of composite call
+*/
+
+/*
+ a composite call moves between the following 3 states.
+*/
+enum composite_state { COMPOSITE_STATE_INIT, /* we are creating the request */
+ COMPOSITE_STATE_IN_PROGRESS, /* the request is in the outgoing socket Q */
+ COMPOSITE_STATE_DONE, /* the request is received by the caller finished */
+ COMPOSITE_STATE_ERROR }; /* a packet or transport level error has occurred */
+
+/* the context of one "composite" call */
+struct composite_context {
+ /* the external state - will be queried by the caller */
+ enum composite_state state;
+
+ /* a private pointer for use by the composite function
+ implementation */
+ void *private_data;
+
+ /* status code when finished */
+ NTSTATUS status;
+
+ /* the event context we are using */
+ struct tevent_context *event_ctx;
+
+ /* information on what to do on completion */
+ struct {
+ void (*fn)(struct composite_context *);
+ void *private_data;
+ } async;
+
+ bool used_wait;
+};
+
+struct irpc_request;
+struct smbcli_request;
+struct smb2_request;
+struct rpc_request;
+struct nbt_name_request;
+
+struct composite_context *composite_create(TALLOC_CTX *mem_ctx, struct tevent_context *ev);
+bool composite_nomem(const void *p, struct composite_context *ctx);
+void composite_continue(struct composite_context *ctx,
+ struct composite_context *new_ctx,
+ void (*continuation)(struct composite_context *),
+ void *private_data);
+void composite_continue_rpc(struct composite_context *ctx,
+ struct rpc_request *new_req,
+ void (*continuation)(struct rpc_request *),
+ void *private_data);
+void composite_continue_irpc(struct composite_context *ctx,
+ struct irpc_request *new_req,
+ void (*continuation)(struct irpc_request *),
+ void *private_data);
+void composite_continue_smb(struct composite_context *ctx,
+ struct smbcli_request *new_req,
+ void (*continuation)(struct smbcli_request *),
+ void *private_data);
+void composite_continue_smb2(struct composite_context *ctx,
+ struct smb2_request *new_req,
+ void (*continuation)(struct smb2_request *),
+ void *private_data);
+void composite_continue_nbt(struct composite_context *ctx,
+ struct nbt_name_request *new_req,
+ void (*continuation)(struct nbt_name_request *),
+ void *private_data);
+bool composite_is_ok(struct composite_context *ctx);
+void composite_done(struct composite_context *ctx);
+void composite_error(struct composite_context *ctx, NTSTATUS status);
+NTSTATUS composite_wait(struct composite_context *c);
+NTSTATUS composite_wait_free(struct composite_context *c);
+
+
+#endif /* __COMPOSITE_H__ */
diff --git a/source4/libcli/config.mk b/source4/libcli/config.mk
new file mode 100644
index 0000000000..dc3431ab9f
--- /dev/null
+++ b/source4/libcli/config.mk
@@ -0,0 +1,169 @@
+mkinclude auth/config.mk
+mkinclude ldap/config.mk
+mkinclude security/config.mk
+mkinclude wbclient/config.mk
+
+[SUBSYSTEM::LIBSAMBA-ERRORS]
+
+LIBSAMBA-ERRORS_OBJ_FILES = $(addprefix ../libcli/util/, doserr.o ) $(libclisrcdir)/util/errormap.o $(libclisrcdir)/util/nterr.o
+
+PUBLIC_HEADERS += $(addprefix ../libcli/util/, error.h ntstatus.h doserr.h werror.h)
+
+[SUBSYSTEM::LIBCLI_LSA]
+PUBLIC_DEPENDENCIES = RPC_NDR_LSA
+PRIVATE_DEPENDENCIES = LIBSECURITY
+
+LIBCLI_LSA_OBJ_FILES = $(libclisrcdir)/util/clilsa.o
+
+$(eval $(call proto_header_template,$(libclisrcdir)/util/clilsa.h,$(LIBCLI_LSA_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LIBCLI_COMPOSITE]
+PUBLIC_DEPENDENCIES = LIBEVENTS
+
+LIBCLI_COMPOSITE_OBJ_FILES = $(libclisrcdir)/composite/composite.o
+$(eval $(call proto_header_template,$(libclisrcdir)/composite/proto.h,$(LIBCLI_COMPOSITE_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LIBCLI_SMB_COMPOSITE]
+PUBLIC_DEPENDENCIES = LIBCLI_COMPOSITE CREDENTIALS gensec LIBCLI_RESOLVE
+
+LIBCLI_SMB_COMPOSITE_OBJ_FILES = $(addprefix $(libclisrcdir)/smb_composite/, \
+ loadfile.o \
+ savefile.o \
+ connect.o \
+ sesssetup.o \
+ fetchfile.o \
+ appendacl.o \
+ fsinfo.o \
+ smb2.o)
+
+$(eval $(call proto_header_template,$(libclisrcdir)/smb_composite/proto.h,$(LIBCLI_SMB_COMPOSITE_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::NDR_NBT_BUF]
+
+NDR_NBT_BUF_OBJ_FILES = $(libclinbtsrcdir)/nbtname.o
+
+$(eval $(call proto_header_template,$(libclinbtsrcdir)/nbtname.h,$(NDR_NBT_BUF_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LIBCLI_NBT]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_NBT LIBCLI_COMPOSITE LIBEVENTS \
+ NDR_SECURITY samba_socket LIBSAMBA-UTIL
+
+LIBCLI_NBT_OBJ_FILES = $(addprefix $(libclinbtsrcdir)/, \
+ nbtsocket.o \
+ namequery.o \
+ nameregister.o \
+ namerefresh.o \
+ namerelease.o)
+
+[BINARY::nmblookup]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ LIBSAMBA-UTIL \
+ LIBCLI_NBT \
+ LIBPOPT \
+ POPT_SAMBA \
+ LIBNETIF \
+ LIBCLI_RESOLVE
+
+nmblookup_OBJ_FILES = $(libclinbtsrcdir)/tools/nmblookup.o
+MANPAGES += $(libclinbtsrcdir)/man/nmblookup.1
+
+[SUBSYSTEM::LIBCLI_NDR_NETLOGON]
+PUBLIC_DEPENDENCIES = LIBNDR \
+ NDR_SECURITY
+
+LIBCLI_NDR_NETLOGON_OBJ_FILES = $(addprefix $(libclinbtsrcdir)/../, ndr_netlogon.o)
+
+[SUBSYSTEM::LIBCLI_NETLOGON]
+PUBLIC_DEPENDENCIES = LIBSAMBA-UTIL LIBCLI_NDR_NETLOGON
+
+LIBCLI_NETLOGON_OBJ_FILES = $(addprefix $(libclinbtsrcdir)/, \
+ ../netlogon.o)
+
+[PYTHON::python_netbios]
+LIBRARY_REALNAME = samba/netbios.$(SHLIBEXT)
+PUBLIC_DEPENDENCIES = LIBCLI_NBT DYNCONFIG LIBSAMBA-HOSTCONFIG
+
+python_netbios_OBJ_FILES = $(libclinbtsrcdir)/pynbt.o
+
+[SUBSYSTEM::LIBCLI_DGRAM]
+PUBLIC_DEPENDENCIES = LIBCLI_NBT LIBNDR LIBCLI_RESOLVE LIBCLI_NETLOGON
+
+LIBCLI_DGRAM_OBJ_FILES = $(addprefix $(libclisrcdir)/dgram/, \
+ dgramsocket.o \
+ mailslot.o \
+ netlogon.o \
+ browse.o)
+
+[SUBSYSTEM::LIBCLI_CLDAP]
+PUBLIC_DEPENDENCIES = LIBCLI_LDAP
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBLDB LIBCLI_NETLOGON
+
+LIBCLI_CLDAP_OBJ_FILES = $(libclisrcdir)/cldap/cldap.o
+# PUBLIC_HEADERS += $(libclisrcdir)/cldap/cldap.h
+
+[SUBSYSTEM::LIBCLI_WREPL]
+PUBLIC_DEPENDENCIES = NDR_WINSREPL samba_socket LIBEVENTS LIBPACKET
+
+LIBCLI_WREPL_OBJ_FILES = $(libclisrcdir)/wrepl/winsrepl.o
+
+$(eval $(call proto_header_template,$(libclisrcdir)/wrepl/winsrepl_proto.h,$(LIBCLI_WREPL_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LIBCLI_RESOLVE]
+PUBLIC_DEPENDENCIES = NDR_NBT
+
+LIBCLI_RESOLVE_OBJ_FILES = $(libclisrcdir)/resolve/resolve.o
+
+$(eval $(call proto_header_template,$(libclisrcdir)/resolve/proto.h,$(LIBCLI_RESOLVE_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LP_RESOLVE]
+PRIVATE_DEPENDENCIES = LIBCLI_NBT LIBSAMBA-HOSTCONFIG LIBNETIF
+
+LP_RESOLVE_OBJ_FILES = $(addprefix $(libclisrcdir)/resolve/, \
+ bcast.o nbtlist.o wins.o \
+ dns_ex.o \
+ host.o resolve_lp.o)
+
+$(eval $(call proto_header_template,$(libclisrcdir)/resolve/lp_proto.h,$(LP_RESOLVE_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LIBCLI_FINDDCS]
+PUBLIC_DEPENDENCIES = LIBCLI_NBT MESSAGING
+
+LIBCLI_FINDDCS_OBJ_FILES = $(libclisrcdir)/finddcs.o
+
+$(eval $(call proto_header_template,$(libclisrcdir)/finddcs.h,$(LIBCLI_FINDDCS_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LIBCLI_SMB]
+PUBLIC_DEPENDENCIES = LIBCLI_RAW LIBSAMBA-ERRORS LIBCLI_AUTH \
+ LIBCLI_SMB_COMPOSITE LIBCLI_NBT LIBSECURITY LIBCLI_RESOLVE \
+ LIBCLI_DGRAM LIBCLI_SMB2 LIBCLI_FINDDCS samba_socket
+
+LIBCLI_SMB_OBJ_FILES = $(addprefix $(libclisrcdir)/, \
+ clireadwrite.o \
+ cliconnect.o \
+ clifile.o \
+ clilist.o \
+ clitrans2.o \
+ climessage.o \
+ clideltree.o)
+
+$(eval $(call proto_header_template,$(libclisrcdir)/libcli_proto.h,$(LIBCLI_SMB_OBJ_FILES:.o=.c)))
+
+# PUBLIC_HEADERS += $(libclisrcdir)/libcli.h
+
+[SUBSYSTEM::LIBCLI_RAW]
+PRIVATE_DEPENDENCIES = LIBCLI_COMPOSITE LP_RESOLVE gensec LIBCLI_RESOLVE LIBSECURITY LIBNDR
+#LDFLAGS = $(LIBCLI_SMB_COMPOSITE_OUTPUT)
+PUBLIC_DEPENDENCIES = samba_socket LIBPACKET gensec LIBCRYPTO CREDENTIALS
+
+LIBCLI_RAW_OBJ_FILES = $(addprefix $(libclisrcdir)/raw/, rawfile.o smb_signing.o clisocket.o \
+ clitransport.o clisession.o clitree.o clierror.o rawrequest.o \
+ rawreadwrite.o rawsearch.o rawsetfileinfo.o raweas.o rawtrans.o \
+ clioplock.o rawnegotiate.o rawfsinfo.o rawfileinfo.o rawnotify.o \
+ rawioctl.o rawacl.o rawdate.o rawlpq.o rawshadow.o)
+
+
+$(eval $(call proto_header_template,$(libclisrcdir)/raw/raw_proto.h,$(LIBCLI_RAW_OBJ_FILES:.o=.c)))
+
+mkinclude smb2/config.mk
diff --git a/source4/libcli/dgram/browse.c b/source4/libcli/dgram/browse.c
new file mode 100644
index 0000000000..14d8278635
--- /dev/null
+++ b/source4/libcli/dgram/browse.c
@@ -0,0 +1,114 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ handling for browsing dgram requests
+
+ Copyright (C) Jelmer Vernooij 2005
+ Heavily based on ntlogon.c
+
+ 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/dgram/libdgram.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "param/param.h"
+
+NTSTATUS dgram_mailslot_browse_send(struct nbt_dgram_socket *dgmsock,
+ struct nbt_name *dest_name,
+ struct socket_address *dest,
+ struct nbt_name *src_name,
+ struct nbt_browse_packet *request)
+{
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
+
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, dgmsock->iconv_convenience, request,
+ (ndr_push_flags_fn_t)ndr_push_nbt_browse_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE,
+ NBT_MAILSLOT_BROWSE,
+ dest_name, dest,
+ src_name, &blob);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+NTSTATUS dgram_mailslot_browse_reply(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *request,
+ const char *mailslot_name,
+ const char *my_netbios_name,
+ struct nbt_browse_packet *reply)
+{
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
+ struct nbt_name myname;
+ struct socket_address *dest;
+
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, dgmsock->iconv_convenience, reply,
+ (ndr_push_flags_fn_t)ndr_push_nbt_browse_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ make_nbt_name_client(&myname, my_netbios_name);
+
+ dest = socket_address_from_strings(tmp_ctx, dgmsock->sock->backend_name,
+ request->src_addr, request->src_port);
+ if (!dest) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE,
+ mailslot_name,
+ &request->data.msg.source_name,
+ dest,
+ &myname, &blob);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+NTSTATUS dgram_mailslot_browse_parse(struct dgram_mailslot_handler *dgmslot,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_browse_packet *pkt)
+{
+ DATA_BLOB data = dgram_mailslot_data(dgram);
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_pull_struct_blob(&data, mem_ctx, dgmslot->dgmsock->iconv_convenience, pkt,
+ (ndr_pull_flags_fn_t)ndr_pull_nbt_browse_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("Failed to parse browse packet of length %d: %s\n",
+ (int)data.length, nt_errstr(status)));
+ if (DEBUGLVL(10)) {
+ file_save("browse.dat", data.data, data.length);
+ }
+ return status;
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/dgram/dgramsocket.c b/source4/libcli/dgram/dgramsocket.c
new file mode 100644
index 0000000000..751706d2c5
--- /dev/null
+++ b/source4/libcli/dgram/dgramsocket.c
@@ -0,0 +1,245 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ low level socket handling for nbt dgram requests (UDP138)
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/dgram/libdgram.h"
+#include "lib/socket/socket.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+
+
+/*
+ handle recv events on a nbt dgram socket
+*/
+static void dgm_socket_recv(struct nbt_dgram_socket *dgmsock)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
+ NTSTATUS status;
+ struct socket_address *src;
+ DATA_BLOB blob;
+ size_t nread, dsize;
+ struct nbt_dgram_packet *packet;
+ const char *mailslot_name;
+ enum ndr_err_code ndr_err;
+
+ status = socket_pending(dgmsock->sock, &dsize);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ blob = data_blob_talloc(tmp_ctx, NULL, dsize);
+ if (blob.data == NULL) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ status = socket_recvfrom(dgmsock->sock, blob.data, blob.length, &nread,
+ tmp_ctx, &src);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+ blob.length = nread;
+
+ DEBUG(2,("Received dgram packet of length %d from %s:%d\n",
+ (int)blob.length, src->addr, src->port));
+
+ packet = talloc(tmp_ctx, struct nbt_dgram_packet);
+ if (packet == NULL) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ /* parse the request */
+ ndr_err = ndr_pull_struct_blob(&blob, packet, dgmsock->iconv_convenience, packet,
+ (ndr_pull_flags_fn_t)ndr_pull_nbt_dgram_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(2,("Failed to parse incoming NBT DGRAM packet - %s\n",
+ nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ /* if this is a mailslot message, then see if we can dispatch it to a handler */
+ mailslot_name = dgram_mailslot_name(packet);
+ if (mailslot_name) {
+ struct dgram_mailslot_handler *dgmslot;
+ dgmslot = dgram_mailslot_find(dgmsock, mailslot_name);
+ if (dgmslot) {
+ dgmslot->handler(dgmslot, packet, src);
+ } else {
+ DEBUG(2,("No mailslot handler for '%s'\n", mailslot_name));
+ }
+ } else {
+ /* dispatch if there is a general handler */
+ if (dgmsock->incoming.handler) {
+ dgmsock->incoming.handler(dgmsock, packet, src);
+ }
+ }
+
+ talloc_free(tmp_ctx);
+}
+
+
+/*
+ handle send events on a nbt dgram socket
+*/
+static void dgm_socket_send(struct nbt_dgram_socket *dgmsock)
+{
+ struct nbt_dgram_request *req;
+ NTSTATUS status;
+
+ while ((req = dgmsock->send_queue)) {
+ size_t len;
+
+ len = req->encoded.length;
+ status = socket_sendto(dgmsock->sock, &req->encoded, &len,
+ req->dest);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(3,("Failed to send datagram of length %u to %s:%d: %s\n",
+ (unsigned)req->encoded.length, req->dest->addr, req->dest->port,
+ nt_errstr(status)));
+ DLIST_REMOVE(dgmsock->send_queue, req);
+ talloc_free(req);
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) return;
+
+ DLIST_REMOVE(dgmsock->send_queue, req);
+ talloc_free(req);
+ }
+
+ EVENT_FD_NOT_WRITEABLE(dgmsock->fde);
+ return;
+}
+
+
+/*
+ handle fd events on a nbt_dgram_socket
+*/
+static void dgm_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct nbt_dgram_socket *dgmsock = talloc_get_type(private_data,
+ struct nbt_dgram_socket);
+ if (flags & EVENT_FD_WRITE) {
+ dgm_socket_send(dgmsock);
+ }
+ if (flags & EVENT_FD_READ) {
+ dgm_socket_recv(dgmsock);
+ }
+}
+
+/*
+ initialise a nbt_dgram_socket. The event_ctx is optional, if provided
+ then operations will use that event context
+*/
+struct nbt_dgram_socket *nbt_dgram_socket_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct smb_iconv_convenience *iconv_convenience)
+{
+ struct nbt_dgram_socket *dgmsock;
+ NTSTATUS status;
+
+ dgmsock = talloc(mem_ctx, struct nbt_dgram_socket);
+ if (dgmsock == NULL) goto failed;
+
+ dgmsock->event_ctx = talloc_reference(dgmsock, event_ctx);
+ if (dgmsock->event_ctx == NULL) goto failed;
+
+ status = socket_create("ip", SOCKET_TYPE_DGRAM, &dgmsock->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ socket_set_option(dgmsock->sock, "SO_BROADCAST", "1");
+
+ talloc_steal(dgmsock, dgmsock->sock);
+
+ dgmsock->fde = event_add_fd(dgmsock->event_ctx, dgmsock,
+ socket_get_fd(dgmsock->sock), 0,
+ dgm_socket_handler, dgmsock);
+
+ dgmsock->send_queue = NULL;
+ dgmsock->incoming.handler = NULL;
+ dgmsock->mailslot_handlers = NULL;
+ dgmsock->iconv_convenience = iconv_convenience;
+
+ return dgmsock;
+
+failed:
+ talloc_free(dgmsock);
+ return NULL;
+}
+
+
+/*
+ setup a handler for generic incoming requests
+*/
+NTSTATUS dgram_set_incoming_handler(struct nbt_dgram_socket *dgmsock,
+ void (*handler)(struct nbt_dgram_socket *,
+ struct nbt_dgram_packet *,
+ struct socket_address *),
+ void *private_data)
+{
+ dgmsock->incoming.handler = handler;
+ dgmsock->incoming.private_data = private_data;
+ EVENT_FD_READABLE(dgmsock->fde);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ queue a datagram for send
+*/
+NTSTATUS nbt_dgram_send(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *packet,
+ struct socket_address *dest)
+{
+ struct nbt_dgram_request *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ enum ndr_err_code ndr_err;
+
+ req = talloc(dgmsock, struct nbt_dgram_request);
+ if (req == NULL) goto failed;
+
+ req->dest = dest;
+ if (talloc_reference(req, dest) == NULL) goto failed;
+
+ ndr_err = ndr_push_struct_blob(&req->encoded, req, dgmsock->iconv_convenience, packet,
+ (ndr_push_flags_fn_t)ndr_push_nbt_dgram_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto failed;
+ }
+
+ DLIST_ADD_END(dgmsock->send_queue, req, struct nbt_dgram_request *);
+
+ EVENT_FD_WRITEABLE(dgmsock->fde);
+
+ return NT_STATUS_OK;
+
+failed:
+ talloc_free(req);
+ return status;
+}
diff --git a/source4/libcli/dgram/libdgram.h b/source4/libcli/dgram/libdgram.h
new file mode 100644
index 0000000000..a17a6042d9
--- /dev/null
+++ b/source4/libcli/dgram/libdgram.h
@@ -0,0 +1,157 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a raw async NBT DGRAM library
+
+ Copyright (C) Andrew Tridgell 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/netlogon.h"
+
+/*
+ a datagram name request
+*/
+struct nbt_dgram_request {
+ struct nbt_dgram_request *next, *prev;
+
+ /* where to send the request */
+ struct socket_address *dest;
+
+ /* the encoded request */
+ DATA_BLOB encoded;
+};
+
+/*
+ context structure for operations on dgram packets
+*/
+struct nbt_dgram_socket {
+ struct socket_context *sock;
+ struct tevent_context *event_ctx;
+ struct smb_iconv_convenience *iconv_convenience;
+
+ /* the fd event */
+ struct tevent_fd *fde;
+
+ /* a queue of outgoing requests */
+ struct nbt_dgram_request *send_queue;
+
+ /* a list of mailslot handlers */
+ struct dgram_mailslot_handler *mailslot_handlers;
+
+ /* what to do with incoming request packets */
+ struct {
+ void (*handler)(struct nbt_dgram_socket *, struct nbt_dgram_packet *,
+ struct socket_address *src);
+ void *private_data;
+ } incoming;
+};
+
+
+/*
+ the mailslot code keeps a list of mailslot handlers. A mailslot
+ handler is a function that receives incoming packets for a specific
+ mailslot name. When a caller needs to send a mailslot and wants to
+ get a reply then it needs to register itself as listening for
+ incoming packets on the reply mailslot
+*/
+
+typedef void (*dgram_mailslot_handler_t)(struct dgram_mailslot_handler *,
+ struct nbt_dgram_packet *,
+ struct socket_address *src);
+
+struct dgram_mailslot_handler {
+ struct dgram_mailslot_handler *next, *prev;
+
+ struct nbt_dgram_socket *dgmsock;
+ const char *mailslot_name;
+
+ dgram_mailslot_handler_t handler;
+ void *private_data;
+};
+
+
+/* prototypes */
+NTSTATUS nbt_dgram_send(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *packet,
+ struct socket_address *dest);
+NTSTATUS dgram_set_incoming_handler(struct nbt_dgram_socket *dgmsock,
+ void (*handler)(struct nbt_dgram_socket *,
+ struct nbt_dgram_packet *,
+ struct socket_address *),
+ void *private_data);
+struct nbt_dgram_socket *nbt_dgram_socket_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct smb_iconv_convenience *);
+
+const char *dgram_mailslot_name(struct nbt_dgram_packet *packet);
+struct dgram_mailslot_handler *dgram_mailslot_find(struct nbt_dgram_socket *dgmsock,
+ const char *mailslot_name);
+struct dgram_mailslot_handler *dgram_mailslot_listen(struct nbt_dgram_socket *dgmsock,
+ const char *mailslot_name,
+ dgram_mailslot_handler_t handler,
+ void *private_data);
+struct dgram_mailslot_handler *dgram_mailslot_temp(struct nbt_dgram_socket *dgmsock,
+ const char *mailslot_name,
+ dgram_mailslot_handler_t handler,
+ void *private_data);
+DATA_BLOB dgram_mailslot_data(struct nbt_dgram_packet *dgram);
+
+
+NTSTATUS dgram_mailslot_send(struct nbt_dgram_socket *dgmsock,
+ enum dgram_msg_type msg_type,
+ const char *mailslot_name,
+ struct nbt_name *dest_name,
+ struct socket_address *dest,
+ struct nbt_name *src_name,
+ DATA_BLOB *request);
+
+NTSTATUS dgram_mailslot_netlogon_send(struct nbt_dgram_socket *dgmsock,
+ struct nbt_name *dest_name,
+ struct socket_address *dest,
+ const char *mailslot_name,
+ struct nbt_name *src_name,
+ struct nbt_netlogon_packet *request);
+NTSTATUS dgram_mailslot_netlogon_reply(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *request,
+ const char *my_netbios_name,
+ const char *mailslot_name,
+ struct nbt_netlogon_response *reply);
+NTSTATUS dgram_mailslot_netlogon_parse_request(struct dgram_mailslot_handler *dgmslot,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_netlogon_packet *netlogon);
+
+NTSTATUS dgram_mailslot_netlogon_parse_response(struct dgram_mailslot_handler *dgmslot,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_netlogon_response *netlogon);
+
+NTSTATUS dgram_mailslot_browse_send(struct nbt_dgram_socket *dgmsock,
+ struct nbt_name *dest_name,
+ struct socket_address *dest,
+ struct nbt_name *src_name,
+ struct nbt_browse_packet *request);
+
+NTSTATUS dgram_mailslot_browse_reply(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *request,
+ const char *mailslot_name,
+ const char *my_netbios_name,
+ struct nbt_browse_packet *reply);
+
+NTSTATUS dgram_mailslot_browse_parse(struct dgram_mailslot_handler *dgmslot,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_browse_packet *pkt);
diff --git a/source4/libcli/dgram/mailslot.c b/source4/libcli/dgram/mailslot.c
new file mode 100644
index 0000000000..261946e458
--- /dev/null
+++ b/source4/libcli/dgram/mailslot.c
@@ -0,0 +1,226 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ packet handling for mailslot requests.
+
+ Copyright (C) Andrew Tridgell 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/>.
+*/
+
+/*
+ This implements "Class 2 mailslots", i.e. the communication mechanism
+ used for all mailslot packets smaller than 425 bytes.
+
+ "Class 1 mailslots" (which use SMB) are used for messages larger
+ than 426 bytes and are supported on some systems. These are not implemented
+ in Samba4 yet, as there don't appear to be any core services that use
+ them.
+
+ 425 and 426-byte sized messages are not supported at all.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/dgram/libdgram.h"
+#include "lib/socket/socket.h"
+
+/*
+ destroy a mailslot handler
+*/
+static int dgram_mailslot_destructor(struct dgram_mailslot_handler *dgmslot)
+{
+ DLIST_REMOVE(dgmslot->dgmsock->mailslot_handlers, dgmslot);
+ return 0;
+}
+
+/*
+ start listening on a mailslot. talloc_free() the handle to stop listening
+*/
+struct dgram_mailslot_handler *dgram_mailslot_listen(struct nbt_dgram_socket *dgmsock,
+ const char *mailslot_name,
+ dgram_mailslot_handler_t handler,
+ void *private_data)
+{
+ struct dgram_mailslot_handler *dgmslot;
+
+ dgmslot = talloc(dgmsock, struct dgram_mailslot_handler);
+ if (dgmslot == NULL) return NULL;
+
+ dgmslot->dgmsock = dgmsock;
+ dgmslot->mailslot_name = talloc_strdup(dgmslot, mailslot_name);
+ if (dgmslot->mailslot_name == NULL) {
+ talloc_free(dgmslot);
+ return NULL;
+ }
+ dgmslot->handler = handler;
+ dgmslot->private_data = private_data;
+
+ DLIST_ADD(dgmsock->mailslot_handlers, dgmslot);
+ talloc_set_destructor(dgmslot, dgram_mailslot_destructor);
+
+ EVENT_FD_READABLE(dgmsock->fde);
+
+ return dgmslot;
+}
+
+/*
+ find the handler for a specific mailslot name
+*/
+struct dgram_mailslot_handler *dgram_mailslot_find(struct nbt_dgram_socket *dgmsock,
+ const char *mailslot_name)
+{
+ struct dgram_mailslot_handler *h;
+ for (h=dgmsock->mailslot_handlers;h;h=h->next) {
+ if (strcasecmp(h->mailslot_name, mailslot_name) == 0) {
+ return h;
+ }
+ }
+ return NULL;
+}
+
+/*
+ check that a datagram packet is a valid mailslot request, and return the
+ mailslot name if it is, otherwise return NULL
+*/
+const char *dgram_mailslot_name(struct nbt_dgram_packet *packet)
+{
+ if (packet->msg_type != DGRAM_DIRECT_UNIQUE &&
+ packet->msg_type != DGRAM_DIRECT_GROUP &&
+ packet->msg_type != DGRAM_BCAST) {
+ return NULL;
+ }
+ if (packet->data.msg.dgram_body_type != DGRAM_SMB) return NULL;
+ if (packet->data.msg.body.smb.smb_command != SMB_TRANSACTION) return NULL;
+ return packet->data.msg.body.smb.body.trans.mailslot_name;
+}
+
+
+/*
+ create a temporary mailslot handler for a reply mailslot, allocating
+ a new mailslot name using the given base name and a random integer extension
+*/
+struct dgram_mailslot_handler *dgram_mailslot_temp(struct nbt_dgram_socket *dgmsock,
+ const char *mailslot_name,
+ dgram_mailslot_handler_t handler,
+ void *private_data)
+{
+ char *name;
+ int i;
+ struct dgram_mailslot_handler *dgmslot;
+
+ /* try a 100 times at most */
+ for (i=0;i<100;i++) {
+ name = talloc_asprintf(dgmsock, "%s%03u",
+ mailslot_name,
+ generate_random() % 1000);
+ if (name == NULL) return NULL;
+ if (dgram_mailslot_find(dgmsock, name)) {
+ talloc_free(name);
+ return NULL;
+ }
+ dgmslot = dgram_mailslot_listen(dgmsock, name, handler, private_data);
+ talloc_free(name);
+ if (dgmslot != NULL) {
+ return dgmslot;
+ }
+ }
+ DEBUG(2,("Unable to create temporary mailslot from %s\n", mailslot_name));
+ return NULL;
+}
+
+
+/*
+ send a mailslot request
+*/
+NTSTATUS dgram_mailslot_send(struct nbt_dgram_socket *dgmsock,
+ enum dgram_msg_type msg_type,
+ const char *mailslot_name,
+ struct nbt_name *dest_name,
+ struct socket_address *dest,
+ struct nbt_name *src_name,
+ DATA_BLOB *request)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
+ struct nbt_dgram_packet packet;
+ struct dgram_message *msg;
+ struct dgram_smb_packet *smb;
+ struct smb_trans_body *trans;
+ struct socket_address *src;
+ NTSTATUS status;
+
+ if (dest->port == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ZERO_STRUCT(packet);
+ packet.msg_type = msg_type;
+ packet.flags = DGRAM_FLAG_FIRST | DGRAM_NODE_NBDD;
+ packet.dgram_id = generate_random() % UINT16_MAX;
+ src = socket_get_my_addr(dgmsock->sock, tmp_ctx);
+ if (!src) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ packet.src_addr = src->addr;
+ packet.src_port = src->port;
+
+ msg = &packet.data.msg;
+ /* this length calculation is very crude - it should be based on gensize
+ calls */
+ msg->length = 138 + strlen(mailslot_name) + request->length;
+ msg->offset = 0;
+
+ msg->source_name = *src_name;
+ msg->dest_name = *dest_name;
+ msg->dgram_body_type = DGRAM_SMB;
+
+ smb = &msg->body.smb;
+ smb->smb_command = SMB_TRANSACTION;
+
+ trans = &smb->body.trans;
+ trans->total_data_count = request->length;
+ trans->timeout = 1000;
+ trans->data_count = request->length;
+ trans->data_offset = 70 + strlen(mailslot_name);
+ trans->opcode = 1; /* write mail slot */
+ trans->priority = 1;
+ trans->_class = 2;
+ trans->mailslot_name = mailslot_name;
+ trans->data = *request;
+
+ status = nbt_dgram_send(dgmsock, &packet, dest);
+
+ talloc_free(tmp_ctx);
+
+ return status;
+}
+
+/*
+ return the mailslot data portion from a mailslot packet
+*/
+DATA_BLOB dgram_mailslot_data(struct nbt_dgram_packet *dgram)
+{
+ struct smb_trans_body *trans = &dgram->data.msg.body.smb.body.trans;
+ DATA_BLOB ret = trans->data;
+ int pad = trans->data_offset - (70 + strlen(trans->mailslot_name));
+
+ if (pad < 0 || pad > ret.length) {
+ DEBUG(2,("Badly formatted data in mailslot - pad = %d\n", pad));
+ return data_blob(NULL, 0);
+ }
+ ret.data += pad;
+ ret.length -= pad;
+ return ret;
+}
diff --git a/source4/libcli/dgram/netlogon.c b/source4/libcli/dgram/netlogon.c
new file mode 100644
index 0000000000..26b00bdafd
--- /dev/null
+++ b/source4/libcli/dgram/netlogon.c
@@ -0,0 +1,145 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ handling for netlogon dgram requests
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/dgram/libdgram.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+
+/*
+ send a netlogon mailslot request
+*/
+NTSTATUS dgram_mailslot_netlogon_send(struct nbt_dgram_socket *dgmsock,
+ struct nbt_name *dest_name,
+ struct socket_address *dest,
+ const char *mailslot,
+ struct nbt_name *src_name,
+ struct nbt_netlogon_packet *request)
+{
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
+
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx,
+ dgmsock->iconv_convenience,
+ request,
+ (ndr_push_flags_fn_t)ndr_push_nbt_netlogon_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+
+ status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE,
+ mailslot,
+ dest_name, dest,
+ src_name, &blob);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+
+/*
+ send a netlogon mailslot reply
+*/
+NTSTATUS dgram_mailslot_netlogon_reply(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *request,
+ const char *my_netbios_name,
+ const char *mailslot_name,
+ struct nbt_netlogon_response *reply)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
+ struct nbt_name myname;
+ struct socket_address *dest;
+
+ status = push_nbt_netlogon_response(&blob, tmp_ctx, dgmsock->iconv_convenience,
+ reply);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ make_nbt_name_client(&myname, my_netbios_name);
+
+ dest = socket_address_from_strings(tmp_ctx, dgmsock->sock->backend_name,
+ request->src_addr, request->src_port);
+ if (!dest) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE,
+ mailslot_name,
+ &request->data.msg.source_name,
+ dest,
+ &myname, &blob);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+
+/*
+ parse a netlogon response. The packet must be a valid mailslot packet
+*/
+NTSTATUS dgram_mailslot_netlogon_parse_request(struct dgram_mailslot_handler *dgmslot,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_netlogon_packet *netlogon)
+{
+ DATA_BLOB data = dgram_mailslot_data(dgram);
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_pull_struct_blob(&data, mem_ctx, dgmslot->dgmsock->iconv_convenience, netlogon,
+ (ndr_pull_flags_fn_t)ndr_pull_nbt_netlogon_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("Failed to parse netlogon packet of length %d: %s\n",
+ (int)data.length, nt_errstr(status)));
+ if (DEBUGLVL(10)) {
+ file_save("netlogon.dat", data.data, data.length);
+ }
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ parse a netlogon response. The packet must be a valid mailslot packet
+*/
+NTSTATUS dgram_mailslot_netlogon_parse_response(struct dgram_mailslot_handler *dgmslot,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_netlogon_response *netlogon)
+{
+ NTSTATUS status;
+ DATA_BLOB data = dgram_mailslot_data(dgram);
+
+ status = pull_nbt_netlogon_response(&data, mem_ctx, dgmslot->dgmsock->iconv_convenience, netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source4/libcli/finddcs.c b/source4/libcli/finddcs.c
new file mode 100644
index 0000000000..2e4fad9332
--- /dev/null
+++ b/source4/libcli/finddcs.c
@@ -0,0 +1,276 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a composite API for finding a DC and its name
+
+ Copyright (C) Volker Lendecke 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
+
+ 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 "include/includes.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "librpc/gen_ndr/samr.h"
+#include "libcli/composite/composite.h"
+#include "libcli/libcli.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/finddcs.h"
+
+struct finddcs_state {
+ struct composite_context *ctx;
+ struct messaging_context *msg_ctx;
+
+ const char *my_netbios_name;
+ const char *domain_name;
+ struct dom_sid *domain_sid;
+
+ struct nbtd_getdcname r;
+ struct nbt_name_status node_status;
+
+ struct smb_iconv_convenience *iconv_convenience;
+
+ int num_dcs;
+ struct nbt_dc_name *dcs;
+ uint16_t nbt_port;
+};
+
+static void finddcs_name_resolved(struct composite_context *ctx);
+static void finddcs_getdc_replied(struct irpc_request *ireq);
+static void fallback_node_status(struct finddcs_state *state);
+static void fallback_node_status_replied(struct nbt_name_request *name_req);
+
+/*
+ * Setup and send off the a normal name resolution for the target name.
+ *
+ * The domain_sid parameter is optional, and is used in the subsequent getdc request.
+ *
+ * This will try a GetDC request, but this may not work. It will try
+ * a node status as a fallback, then return no name (but still include
+ * the IP)
+ */
+
+struct composite_context *finddcs_send(TALLOC_CTX *mem_ctx,
+ const char *my_netbios_name,
+ uint16_t nbt_port,
+ const char *domain_name,
+ int name_type,
+ struct dom_sid *domain_sid,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct messaging_context *msg_ctx)
+{
+ struct composite_context *c, *creq;
+ struct finddcs_state *state;
+ struct nbt_name name;
+
+ c = composite_create(mem_ctx, event_ctx);
+ if (c == NULL) return NULL;
+
+ state = talloc(c, struct finddcs_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->ctx = c;
+
+ state->nbt_port = nbt_port;
+ state->my_netbios_name = talloc_strdup(state, my_netbios_name);
+ state->domain_name = talloc_strdup(state, domain_name);
+ state->iconv_convenience = iconv_convenience;
+ if (composite_nomem(state->domain_name, c)) return c;
+
+ if (domain_sid) {
+ state->domain_sid = talloc_reference(state, domain_sid);
+ if (composite_nomem(state->domain_sid, c)) return c;
+ } else {
+ state->domain_sid = NULL;
+ }
+
+ state->msg_ctx = msg_ctx;
+
+ make_nbt_name(&name, state->domain_name, name_type);
+ creq = resolve_name_send(resolve_ctx, &name, event_ctx);
+ composite_continue(c, creq, finddcs_name_resolved, state);
+ return c;
+}
+
+/* Having got an name query answer, fire off a GetDC request, so we
+ * can find the target's all-important name. (Kerberos and some
+ * netlogon operations are quite picky about names)
+ *
+ * The name is a courtesy, if we don't find it, don't completely fail.
+ *
+ * However, if the nbt server is down, fall back to a node status
+ * request
+ */
+static void finddcs_name_resolved(struct composite_context *ctx)
+{
+ struct finddcs_state *state =
+ talloc_get_type(ctx->async.private_data, struct finddcs_state);
+ struct irpc_request *ireq;
+ struct server_id *nbt_servers;
+ const char *address;
+
+ state->ctx->status = resolve_name_recv(ctx, state, &address);
+ if (!composite_is_ok(state->ctx)) return;
+
+ /* TODO: This should try and find all the DCs, and give the
+ * caller them in the order they responded */
+
+ state->num_dcs = 1;
+ state->dcs = talloc_array(state, struct nbt_dc_name, state->num_dcs);
+ if (composite_nomem(state->dcs, state->ctx)) return;
+
+ state->dcs[0].address = talloc_steal(state->dcs, address);
+
+ /* Try and find the nbt server. Fallback to a node status
+ * request if we can't make this happen The nbt server just
+ * might not be running, or we may not have a messaging
+ * context (not root etc) */
+ if (!state->msg_ctx) {
+ fallback_node_status(state);
+ return;
+ }
+
+ nbt_servers = irpc_servers_byname(state->msg_ctx, state, "nbt_server");
+ if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
+ fallback_node_status(state);
+ return;
+ }
+
+ state->r.in.domainname = state->domain_name;
+ state->r.in.ip_address = state->dcs[0].address;
+ state->r.in.my_computername = state->my_netbios_name;
+ state->r.in.my_accountname = talloc_asprintf(state, "%s$", state->my_netbios_name);
+ if (composite_nomem(state->r.in.my_accountname, state->ctx)) return;
+ state->r.in.account_control = ACB_WSTRUST;
+ state->r.in.domain_sid = state->domain_sid;
+
+ ireq = irpc_call_send(state->msg_ctx, nbt_servers[0],
+ &ndr_table_irpc, NDR_NBTD_GETDCNAME,
+ &state->r, state);
+ if (!ireq) {
+ fallback_node_status(state);
+ return;
+ }
+
+ composite_continue_irpc(state->ctx, ireq, finddcs_getdc_replied, state);
+}
+
+/* Called when the GetDC request returns */
+static void finddcs_getdc_replied(struct irpc_request *ireq)
+{
+ struct finddcs_state *state =
+ talloc_get_type(ireq->async.private_data, struct finddcs_state);
+
+ state->ctx->status = irpc_call_recv(ireq);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->dcs[0].name = talloc_steal(state->dcs, state->r.out.dcname);
+ composite_done(state->ctx);
+}
+
+/* The GetDC request might not be available (such as occours when the
+ * NBT server is down). Fallback to a node status. It is the best
+ * hope we have... */
+static void fallback_node_status(struct finddcs_state *state)
+{
+ struct nbt_name_socket *nbtsock;
+ struct nbt_name_request *name_req;
+
+ state->node_status.in.name.name = "*";
+ state->node_status.in.name.type = NBT_NAME_CLIENT;
+ state->node_status.in.name.scope = NULL;
+ state->node_status.in.dest_addr = state->dcs[0].address;
+ state->node_status.in.dest_port = state->nbt_port;
+ state->node_status.in.timeout = 1;
+ state->node_status.in.retries = 2;
+
+ nbtsock = nbt_name_socket_init(state, state->ctx->event_ctx,
+ state->iconv_convenience);
+ if (composite_nomem(nbtsock, state->ctx)) return;
+
+ name_req = nbt_name_status_send(nbtsock, &state->node_status);
+ if (composite_nomem(name_req, state->ctx)) return;
+
+ composite_continue_nbt(state->ctx,
+ name_req,
+ fallback_node_status_replied,
+ state);
+}
+
+/* We have a node status reply (or perhaps a timeout) */
+static void fallback_node_status_replied(struct nbt_name_request *name_req)
+{
+ int i;
+ struct finddcs_state *state = talloc_get_type(name_req->async.private_data, struct finddcs_state);
+ state->ctx->status = nbt_name_status_recv(name_req, state, &state->node_status);
+ if (!composite_is_ok(state->ctx)) return;
+
+ for (i=0; i < state->node_status.out.status.num_names; i++) {
+ int j;
+ if (state->node_status.out.status.names[i].type == NBT_NAME_SERVER) {
+ char *name = talloc_strndup(state->dcs, state->node_status.out.status.names[0].name, 15);
+ /* Strip space padding */
+ if (name) {
+ j = MIN(strlen(name), 15);
+ for (; j > 0 && name[j - 1] == ' '; j--) {
+ name[j - 1] = '\0';
+ }
+ }
+ state->dcs[0].name = name;
+ composite_done(state->ctx);
+ return;
+ }
+ }
+ composite_error(state->ctx, NT_STATUS_NO_LOGON_SERVERS);
+}
+
+NTSTATUS finddcs_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ int *num_dcs, struct nbt_dc_name **dcs)
+{
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ struct finddcs_state *state =
+ talloc_get_type(c->private_data, struct finddcs_state);
+ *num_dcs = state->num_dcs;
+ *dcs = talloc_steal(mem_ctx, state->dcs);
+ }
+ talloc_free(c);
+ return status;
+}
+
+NTSTATUS finddcs(TALLOC_CTX *mem_ctx,
+ const char *my_netbios_name,
+ uint16_t nbt_port,
+ const char *domain_name, int name_type,
+ struct dom_sid *domain_sid,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct messaging_context *msg_ctx,
+ int *num_dcs, struct nbt_dc_name **dcs)
+{
+ struct composite_context *c = finddcs_send(mem_ctx,
+ my_netbios_name,
+ nbt_port,
+ domain_name, name_type,
+ domain_sid,
+ iconv_convenience,
+ resolve_ctx,
+ event_ctx, msg_ctx);
+ return finddcs_recv(c, mem_ctx, num_dcs, dcs);
+}
diff --git a/source4/libcli/ldap/config.mk b/source4/libcli/ldap/config.mk
new file mode 100644
index 0000000000..f0c0f5295d
--- /dev/null
+++ b/source4/libcli/ldap/config.mk
@@ -0,0 +1,12 @@
+[SUBSYSTEM::LIBCLI_LDAP]
+PUBLIC_DEPENDENCIES = LIBSAMBA-ERRORS LIBTEVENT LIBPACKET
+PRIVATE_DEPENDENCIES = LIBCLI_COMPOSITE samba_socket NDR_SAMR LIBTLS \
+ LIBCLI_LDAP_NDR LIBNDR LP_RESOLVE gensec LIBCLI_LDAP_MESSAGE
+
+LIBCLI_LDAP_OBJ_FILES = $(addprefix $(libclisrcdir)/ldap/, \
+ ldap_client.o ldap_bind.o \
+ ldap_ildap.o ldap_controls.o)
+PUBLIC_HEADERS += $(libclisrcdir)/ldap/ldap.h
+
+$(eval $(call proto_header_template,$(libclisrcdir)/ldap/ldap_proto.h,$(LIBCLI_LDAP_OBJ_FILES:.o=.c)))
+
diff --git a/source4/libcli/ldap/ldap.h b/source4/libcli/ldap/ldap.h
new file mode 100644
index 0000000000..79cfef2128
--- /dev/null
+++ b/source4/libcli/ldap/ldap.h
@@ -0,0 +1,31 @@
+/*
+ Unix SMB/CIFS Implementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Volker Lendecke 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/>.
+
+*/
+
+#ifndef _SMB_LDAP_H_
+#define _SMB_LDAP_H_
+
+#include "../libcli/ldap/ldap_message.h"
+#include "librpc/gen_ndr/misc.h"
+
+struct tevent_context;
+struct cli_credentials;
+struct dom_sid;
+
+#endif
diff --git a/source4/libcli/ldap/ldap_bind.c b/source4/libcli/ldap/ldap_bind.c
new file mode 100644
index 0000000000..5e6a5faafa
--- /dev/null
+++ b/source4/libcli/ldap/ldap_bind.c
@@ -0,0 +1,413 @@
+/*
+ Unix SMB/CIFS mplementation.
+
+ LDAP bind calls
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Volker Lendecke 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/ldap/ldap.h"
+#include "libcli/ldap/ldap_proto.h"
+#include "libcli/ldap/ldap_client.h"
+#include "lib/tls/tls.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "lib/stream/packet.h"
+#include "param/param.h"
+
+struct ldap_simple_creds {
+ const char *dn;
+ const char *pw;
+};
+
+_PUBLIC_ NTSTATUS ldap_rebind(struct ldap_connection *conn)
+{
+ NTSTATUS status;
+ struct ldap_simple_creds *creds;
+
+ switch (conn->bind.type) {
+ case LDAP_BIND_SASL:
+ status = ldap_bind_sasl(conn, (struct cli_credentials *)conn->bind.creds,
+ conn->lp_ctx);
+ break;
+
+ case LDAP_BIND_SIMPLE:
+ creds = (struct ldap_simple_creds *)conn->bind.creds;
+
+ if (creds == NULL) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = ldap_bind_simple(conn, creds->dn, creds->pw);
+ break;
+
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return status;
+}
+
+
+static struct ldap_message *new_ldap_simple_bind_msg(struct ldap_connection *conn,
+ const char *dn, const char *pw)
+{
+ struct ldap_message *res;
+
+ res = new_ldap_message(conn);
+ if (!res) {
+ return NULL;
+ }
+
+ res->type = LDAP_TAG_BindRequest;
+ res->r.BindRequest.version = 3;
+ res->r.BindRequest.dn = talloc_strdup(res, dn);
+ res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SIMPLE;
+ res->r.BindRequest.creds.password = talloc_strdup(res, pw);
+ res->controls = NULL;
+
+ return res;
+}
+
+
+/*
+ perform a simple username/password bind
+*/
+_PUBLIC_ NTSTATUS ldap_bind_simple(struct ldap_connection *conn,
+ const char *userdn, const char *password)
+{
+ struct ldap_request *req;
+ struct ldap_message *msg;
+ const char *dn, *pw;
+ NTSTATUS status;
+
+ if (conn == NULL) {
+ return NT_STATUS_INVALID_CONNECTION;
+ }
+
+ if (userdn) {
+ dn = userdn;
+ } else {
+ if (conn->auth_dn) {
+ dn = conn->auth_dn;
+ } else {
+ dn = "";
+ }
+ }
+
+ if (password) {
+ pw = password;
+ } else {
+ if (conn->simple_pw) {
+ pw = conn->simple_pw;
+ } else {
+ pw = "";
+ }
+ }
+
+ msg = new_ldap_simple_bind_msg(conn, dn, pw);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ /* send the request */
+ req = ldap_request_send(conn, msg);
+ talloc_free(msg);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ /* wait for replies */
+ status = ldap_request_wait(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return status;
+ }
+
+ /* check its a valid reply */
+ msg = req->replies[0];
+ if (msg->type != LDAP_TAG_BindResponse) {
+ talloc_free(req);
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ status = ldap_check_response(conn, &msg->r.BindResponse.response);
+
+ talloc_free(req);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct ldap_simple_creds *creds = talloc(conn, struct ldap_simple_creds);
+ if (creds == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ creds->dn = talloc_strdup(creds, dn);
+ creds->pw = talloc_strdup(creds, pw);
+ if (creds->dn == NULL || creds->pw == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ conn->bind.type = LDAP_BIND_SIMPLE;
+ conn->bind.creds = creds;
+ }
+
+ return status;
+}
+
+
+static struct ldap_message *new_ldap_sasl_bind_msg(struct ldap_connection *conn,
+ const char *sasl_mechanism,
+ DATA_BLOB *secblob)
+{
+ struct ldap_message *res;
+
+ res = new_ldap_message(conn);
+ if (!res) {
+ return NULL;
+ }
+
+ res->type = LDAP_TAG_BindRequest;
+ res->r.BindRequest.version = 3;
+ res->r.BindRequest.dn = "";
+ res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SASL;
+ res->r.BindRequest.creds.SASL.mechanism = talloc_strdup(res, sasl_mechanism);
+ if (secblob) {
+ res->r.BindRequest.creds.SASL.secblob = talloc(res, DATA_BLOB);
+ if (!res->r.BindRequest.creds.SASL.secblob) {
+ talloc_free(res);
+ return NULL;
+ }
+ *res->r.BindRequest.creds.SASL.secblob = *secblob;
+ } else {
+ res->r.BindRequest.creds.SASL.secblob = NULL;
+ }
+ res->controls = NULL;
+
+ return res;
+}
+
+
+/*
+ perform a sasl bind using the given credentials
+*/
+_PUBLIC_ NTSTATUS ldap_bind_sasl(struct ldap_connection *conn,
+ struct cli_credentials *creds,
+ struct loadparm_context *lp_ctx)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ DATA_BLOB input = data_blob(NULL, 0);
+ DATA_BLOB output = data_blob(NULL, 0);
+
+ struct ldap_message **sasl_mechs_msgs;
+ struct ldap_SearchResEntry *search;
+ int count, i;
+
+ const char **sasl_names;
+ uint32_t old_gensec_features;
+ static const char *supported_sasl_mech_attrs[] = {
+ "supportedSASLMechanisms",
+ NULL
+ };
+
+ gensec_init(lp_ctx);
+
+ status = gensec_client_start(conn, &conn->gensec,
+ conn->event.event_ctx,
+ lp_gensec_settings(conn, lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ /* require Kerberos SIGN/SEAL only if we don't use SSL
+ * Windows seem not to like double encryption */
+ old_gensec_features = cli_credentials_get_gensec_features(creds);
+ if (tls_enabled(conn->sock)) {
+ cli_credentials_set_gensec_features(creds, old_gensec_features & ~(GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL));
+ }
+
+ /* this call also sets the gensec_want_features */
+ status = gensec_set_credentials(conn->gensec, creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC creds: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+
+ /* reset the original gensec_features (on the credentials
+ * context, so we don't tatoo it ) */
+ cli_credentials_set_gensec_features(creds, old_gensec_features);
+
+ if (conn->host) {
+ status = gensec_set_target_hostname(conn->gensec, conn->host);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC target hostname: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+ }
+
+ status = gensec_set_target_service(conn->gensec, "ldap");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC target service: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+
+ status = ildap_search(conn, "", LDAP_SEARCH_SCOPE_BASE, "", supported_sasl_mech_attrs,
+ false, NULL, NULL, &sasl_mechs_msgs);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+
+ count = ildap_count_entries(conn, sasl_mechs_msgs);
+ if (count != 1) {
+ DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of replies: %d\n",
+ count));
+ goto failed;
+ }
+
+ tmp_ctx = talloc_new(conn);
+ if (tmp_ctx == NULL) goto failed;
+
+ search = &sasl_mechs_msgs[0]->r.SearchResultEntry;
+ if (search->num_attributes != 1) {
+ DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of attributes: %d != 1\n",
+ search->num_attributes));
+ goto failed;
+ }
+
+ sasl_names = talloc_array(tmp_ctx, const char *, search->attributes[0].num_values + 1);
+ if (!sasl_names) {
+ DEBUG(1, ("talloc_arry(char *, %d) failed\n",
+ count));
+ goto failed;
+ }
+
+ for (i=0; i<search->attributes[0].num_values; i++) {
+ sasl_names[i] = (const char *)search->attributes[0].values[i].data;
+ }
+ sasl_names[i] = NULL;
+
+ status = gensec_start_mech_by_sasl_list(conn->gensec, sasl_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("None of the %d proposed SASL mechs were acceptable: %s\n",
+ count, nt_errstr(status)));
+ goto failed;
+ }
+
+ while (1) {
+ NTSTATUS gensec_status;
+ struct ldap_message *response;
+ struct ldap_message *msg;
+ struct ldap_request *req;
+ int result = LDAP_OTHER;
+
+ status = gensec_update(conn->gensec, tmp_ctx,
+ input,
+ &output);
+ /* The status value here, from GENSEC is vital to the security
+ * of the system. Even if the other end accepts, if GENSEC
+ * claims 'MORE_PROCESSING_REQUIRED' then you must keep
+ * feeding it blobs, or else the remote host/attacker might
+ * avoid mutal authentication requirements.
+ *
+ * Likewise, you must not feed GENSEC too much (after the OK),
+ * it doesn't like that either
+ */
+
+ gensec_status = status;
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ if (NT_STATUS_IS_OK(status) && output.length == 0) {
+ break;
+ }
+
+ /* Perhaps we should make gensec_start_mech_by_sasl_list() return the name we got? */
+ msg = new_ldap_sasl_bind_msg(tmp_ctx, conn->gensec->ops->sasl_name, (output.data?&output:NULL));
+ if (msg == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ req = ldap_request_send(conn, msg);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ talloc_steal(tmp_ctx, req);
+
+ status = ldap_result_n(req, 0, &response);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ if (response->type != LDAP_TAG_BindResponse) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ goto failed;
+ }
+
+ result = response->r.BindResponse.response.resultcode;
+
+ if (result != LDAP_SUCCESS && result != LDAP_SASL_BIND_IN_PROGRESS) {
+ status = ldap_check_response(conn,
+ &response->r.BindResponse.response);
+ break;
+ }
+
+ /* This is where we check if GENSEC wanted to be fed more data */
+ if (!NT_STATUS_EQUAL(gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ break;
+ }
+ if (response->r.BindResponse.SASL.secblob) {
+ input = *response->r.BindResponse.SASL.secblob;
+ } else {
+ input = data_blob(NULL, 0);
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct socket_context *sasl_socket;
+ status = gensec_socket_init(conn->gensec,
+ conn,
+ conn->sock,
+ conn->event.event_ctx,
+ ldap_read_io_handler,
+ conn,
+ &sasl_socket);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ conn->sock = sasl_socket;
+ packet_set_socket(conn->packet, conn->sock);
+
+ conn->bind.type = LDAP_BIND_SASL;
+ conn->bind.creds = creds;
+ }
+
+ return status;
+
+failed:
+ talloc_free(tmp_ctx);
+ talloc_free(conn->gensec);
+ conn->gensec = NULL;
+ return status;
+}
diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c
new file mode 100644
index 0000000000..304a2e1253
--- /dev/null
+++ b/source4/libcli/ldap/ldap_client.c
@@ -0,0 +1,832 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Simo Sorce 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 <tevent.h>
+#include "lib/socket/socket.h"
+#include "../lib/util/asn1.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/ldap/ldap.h"
+#include "libcli/ldap/ldap_proto.h"
+#include "libcli/ldap/ldap_client.h"
+#include "libcli/composite/composite.h"
+#include "lib/stream/packet.h"
+#include "lib/tls/tls.h"
+#include "auth/gensec/gensec.h"
+#include "system/time.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+/**
+ create a new ldap_connection stucture. The event context is optional
+*/
+_PUBLIC_ struct ldap_connection *ldap4_new_connection(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev)
+{
+ struct ldap_connection *conn;
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ conn = talloc_zero(mem_ctx, struct ldap_connection);
+ if (conn == NULL) {
+ return NULL;
+ }
+
+ conn->next_messageid = 1;
+ conn->event.event_ctx = ev;
+
+ conn->lp_ctx = lp_ctx;
+
+ /* set a reasonable request timeout */
+ conn->timeout = 60;
+
+ /* explicitly avoid reconnections by default */
+ conn->reconnect.max_retries = 0;
+
+ return conn;
+}
+
+/*
+ the connection is dead
+*/
+static void ldap_connection_dead(struct ldap_connection *conn)
+{
+ struct ldap_request *req;
+
+ talloc_free(conn->sock); /* this will also free event.fde */
+ talloc_free(conn->packet);
+ conn->sock = NULL;
+ conn->event.fde = NULL;
+ conn->packet = NULL;
+
+ /* return an error for any pending request ... */
+ while (conn->pending) {
+ req = conn->pending;
+ DLIST_REMOVE(req->conn->pending, req);
+ req->state = LDAP_REQUEST_DONE;
+ req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ }
+}
+
+static void ldap_reconnect(struct ldap_connection *conn);
+
+/*
+ handle packet errors
+*/
+static void ldap_error_handler(void *private_data, NTSTATUS status)
+{
+ struct ldap_connection *conn = talloc_get_type(private_data,
+ struct ldap_connection);
+ ldap_connection_dead(conn);
+
+ /* but try to reconnect so that the ldb client can go on */
+ ldap_reconnect(conn);
+}
+
+
+/*
+ match up with a pending message, adding to the replies list
+*/
+static void ldap_match_message(struct ldap_connection *conn, struct ldap_message *msg)
+{
+ struct ldap_request *req;
+ int i;
+
+ for (req=conn->pending; req; req=req->next) {
+ if (req->messageid == msg->messageid) break;
+ }
+ /* match a zero message id to the last request sent.
+ It seems that servers send 0 if unable to parse */
+ if (req == NULL && msg->messageid == 0) {
+ req = conn->pending;
+ }
+ if (req == NULL) {
+ DEBUG(0,("ldap: no matching message id for %u\n",
+ msg->messageid));
+ talloc_free(msg);
+ return;
+ }
+
+ /* Check for undecoded critical extensions */
+ for (i=0; msg->controls && msg->controls[i]; i++) {
+ if (!msg->controls_decoded[i] &&
+ msg->controls[i]->critical) {
+ req->status = NT_STATUS_LDAP(LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
+ req->state = LDAP_REQUEST_DONE;
+ DLIST_REMOVE(conn->pending, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+ }
+
+ /* add to the list of replies received */
+ talloc_steal(req, msg);
+ req->replies = talloc_realloc(req, req->replies,
+ struct ldap_message *, req->num_replies+1);
+ if (req->replies == NULL) {
+ req->status = NT_STATUS_NO_MEMORY;
+ req->state = LDAP_REQUEST_DONE;
+ DLIST_REMOVE(conn->pending, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+
+ req->replies[req->num_replies] = talloc_steal(req->replies, msg);
+ req->num_replies++;
+
+ if (msg->type != LDAP_TAG_SearchResultEntry &&
+ msg->type != LDAP_TAG_SearchResultReference) {
+ /* currently only search results expect multiple
+ replies */
+ req->state = LDAP_REQUEST_DONE;
+ DLIST_REMOVE(conn->pending, req);
+ }
+
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+
+/*
+ decode/process LDAP data
+*/
+static NTSTATUS ldap_recv_handler(void *private_data, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ struct ldap_connection *conn = talloc_get_type(private_data,
+ struct ldap_connection);
+ struct ldap_message *msg = talloc(conn, struct ldap_message);
+ struct asn1_data *asn1 = asn1_init(conn);
+
+ if (asn1 == NULL || msg == NULL) {
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
+
+ if (!asn1_load(asn1, blob)) {
+ talloc_free(msg);
+ talloc_free(asn1);
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
+
+ status = ldap_decode(asn1, samba_ldap_control_handlers(), msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ asn1_free(asn1);
+ return status;
+ }
+
+ ldap_match_message(conn, msg);
+
+ data_blob_free(&blob);
+ asn1_free(asn1);
+ return NT_STATUS_OK;
+}
+
+/* Handle read events, from the GENSEC socket callback, or real events */
+void ldap_read_io_handler(void *private_data, uint16_t flags)
+{
+ struct ldap_connection *conn = talloc_get_type(private_data,
+ struct ldap_connection);
+ packet_recv(conn->packet);
+}
+
+/*
+ handle ldap socket events
+*/
+static void ldap_io_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct ldap_connection *conn = talloc_get_type(private_data,
+ struct ldap_connection);
+ if (flags & TEVENT_FD_WRITE) {
+ packet_queue_run(conn->packet);
+ if (!tls_enabled(conn->sock)) return;
+ }
+ if (flags & TEVENT_FD_READ) {
+ ldap_read_io_handler(private_data, flags);
+ }
+}
+
+/*
+ parse a ldap URL
+*/
+static NTSTATUS ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url,
+ char **host, uint16_t *port, bool *ldaps)
+{
+ int tmp_port = 0;
+ char protocol[11];
+ char tmp_host[1025];
+ int ret;
+
+ /* Paranoia check */
+ SMB_ASSERT(sizeof(protocol)>10 && sizeof(tmp_host)>254);
+
+ ret = sscanf(url, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port);
+ if (ret < 2) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strequal(protocol, "ldap")) {
+ *port = 389;
+ *ldaps = false;
+ } else if (strequal(protocol, "ldaps")) {
+ *port = 636;
+ *ldaps = true;
+ } else {
+ DEBUG(0, ("unrecognised ldap protocol (%s)!\n", protocol));
+ return NT_STATUS_PROTOCOL_UNREACHABLE;
+ }
+
+ if (tmp_port != 0)
+ *port = tmp_port;
+
+ *host = talloc_strdup(mem_ctx, tmp_host);
+ NT_STATUS_HAVE_NO_MEMORY(*host);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ connect to a ldap server
+*/
+
+struct ldap_connect_state {
+ struct composite_context *ctx;
+ struct ldap_connection *conn;
+};
+
+static void ldap_connect_recv_unix_conn(struct composite_context *ctx);
+static void ldap_connect_recv_tcp_conn(struct composite_context *ctx);
+
+_PUBLIC_ struct composite_context *ldap_connect_send(struct ldap_connection *conn,
+ const char *url)
+{
+ struct composite_context *result, *ctx;
+ struct ldap_connect_state *state;
+ char protocol[11];
+ int ret;
+
+ result = talloc_zero(conn, struct composite_context);
+ if (result == NULL) goto failed;
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+ result->async.fn = NULL;
+ result->event_ctx = conn->event.event_ctx;
+
+ state = talloc(result, struct ldap_connect_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->conn = conn;
+
+ if (conn->reconnect.url == NULL) {
+ conn->reconnect.url = talloc_strdup(conn, url);
+ if (conn->reconnect.url == NULL) goto failed;
+ }
+
+ /* Paranoia check */
+ SMB_ASSERT(sizeof(protocol)>10);
+
+ ret = sscanf(url, "%10[^:]://", protocol);
+ if (ret < 1) {
+ return NULL;
+ }
+
+ if (strequal(protocol, "ldapi")) {
+ struct socket_address *unix_addr;
+ char path[1025];
+
+ NTSTATUS status = socket_create("unix", SOCKET_TYPE_STREAM, &conn->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+ talloc_steal(conn, conn->sock);
+ SMB_ASSERT(sizeof(protocol)>10);
+ SMB_ASSERT(sizeof(path)>1024);
+
+ /* LDAPI connections are to localhost, so give the local host name as the target for gensec */
+ conn->host = talloc_asprintf(conn, "%s.%s", lp_netbios_name(conn->lp_ctx), lp_realm(conn->lp_ctx));
+ if (composite_nomem(conn->host, state->ctx)) {
+ return result;
+ }
+
+ /* The %c specifier doesn't null terminate :-( */
+ ZERO_STRUCT(path);
+ ret = sscanf(url, "%10[^:]://%1025c", protocol, path);
+ if (ret < 2) {
+ composite_error(state->ctx, NT_STATUS_INVALID_PARAMETER);
+ return result;
+ }
+
+ rfc1738_unescape(path);
+
+ unix_addr = socket_address_from_strings(conn, conn->sock->backend_name,
+ path, 0);
+ if (!unix_addr) {
+ return NULL;
+ }
+
+ ctx = socket_connect_send(conn->sock, NULL, unix_addr,
+ 0, conn->event.event_ctx);
+ ctx->async.fn = ldap_connect_recv_unix_conn;
+ ctx->async.private_data = state;
+ return result;
+ } else {
+ NTSTATUS status = ldap_parse_basic_url(conn, url, &conn->host,
+ &conn->port, &conn->ldaps);
+ if (!NT_STATUS_IS_OK(state->ctx->status)) {
+ composite_error(state->ctx, status);
+ return result;
+ }
+
+ ctx = socket_connect_multi_send(state, conn->host, 1, &conn->port,
+ lp_resolve_context(conn->lp_ctx), conn->event.event_ctx);
+ if (ctx == NULL) goto failed;
+
+ ctx->async.fn = ldap_connect_recv_tcp_conn;
+ ctx->async.private_data = state;
+ return result;
+ }
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void ldap_connect_got_sock(struct composite_context *ctx,
+ struct ldap_connection *conn)
+{
+ /* setup a handler for events on this socket */
+ conn->event.fde = tevent_add_fd(conn->event.event_ctx, conn->sock,
+ socket_get_fd(conn->sock),
+ TEVENT_FD_READ, ldap_io_handler, conn);
+ if (conn->event.fde == NULL) {
+ composite_error(ctx, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ tevent_fd_set_close_fn(conn->event.fde, socket_tevent_fd_close_fn);
+ socket_set_flags(conn->sock, SOCKET_FLAG_NOCLOSE);
+
+ talloc_steal(conn, conn->sock);
+ if (conn->ldaps) {
+ struct socket_context *tls_socket;
+ struct socket_context *tmp_socket;
+ char *cafile = lp_tls_cafile(conn->sock, conn->lp_ctx);
+
+ if (!cafile || !*cafile) {
+ talloc_free(conn->sock);
+ return;
+ }
+
+ tls_socket = tls_init_client(conn->sock, conn->event.fde, cafile);
+ talloc_free(cafile);
+
+ if (tls_socket == NULL) {
+ talloc_free(conn->sock);
+ return;
+ }
+
+ /* the original socket, must become a child of the tls socket */
+ tmp_socket = conn->sock;
+ conn->sock = talloc_steal(conn, tls_socket);
+ talloc_steal(conn->sock, tmp_socket);
+ }
+
+ conn->packet = packet_init(conn);
+ if (conn->packet == NULL) {
+ talloc_free(conn->sock);
+ return;
+ }
+
+ packet_set_private(conn->packet, conn);
+ packet_set_socket(conn->packet, conn->sock);
+ packet_set_callback(conn->packet, ldap_recv_handler);
+ packet_set_full_request(conn->packet, ldap_full_packet);
+ packet_set_error_handler(conn->packet, ldap_error_handler);
+ packet_set_event_context(conn->packet, conn->event.event_ctx);
+ packet_set_fde(conn->packet, conn->event.fde);
+/* packet_set_serialise(conn->packet); */
+
+ if (conn->ldaps) {
+ packet_set_unreliable_select(conn->packet);
+ }
+
+ composite_done(ctx);
+}
+
+static void ldap_connect_recv_tcp_conn(struct composite_context *ctx)
+{
+ struct ldap_connect_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct ldap_connect_state);
+ struct ldap_connection *conn = state->conn;
+ uint16_t port;
+ NTSTATUS status = socket_connect_multi_recv(ctx, state, &conn->sock,
+ &port);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ ldap_connect_got_sock(state->ctx, conn);
+}
+
+static void ldap_connect_recv_unix_conn(struct composite_context *ctx)
+{
+ struct ldap_connect_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct ldap_connect_state);
+ struct ldap_connection *conn = state->conn;
+
+ NTSTATUS status = socket_connect_recv(ctx);
+
+ if (!NT_STATUS_IS_OK(state->ctx->status)) {
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ ldap_connect_got_sock(state->ctx, conn);
+}
+
+_PUBLIC_ NTSTATUS ldap_connect_recv(struct composite_context *ctx)
+{
+ NTSTATUS status = composite_wait(ctx);
+ talloc_free(ctx);
+ return status;
+}
+
+_PUBLIC_ NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url)
+{
+ struct composite_context *ctx = ldap_connect_send(conn, url);
+ return ldap_connect_recv(ctx);
+}
+
+/* set reconnect parameters */
+
+_PUBLIC_ void ldap_set_reconn_params(struct ldap_connection *conn, int max_retries)
+{
+ if (conn) {
+ conn->reconnect.max_retries = max_retries;
+ conn->reconnect.retries = 0;
+ conn->reconnect.previous = time(NULL);
+ }
+}
+
+/* Actually this function is NOT ASYNC safe, FIXME? */
+static void ldap_reconnect(struct ldap_connection *conn)
+{
+ NTSTATUS status;
+ time_t now = time(NULL);
+
+ /* do we have set up reconnect ? */
+ if (conn->reconnect.max_retries == 0) return;
+
+ /* is the retry time expired ? */
+ if (now > conn->reconnect.previous + 30) {
+ conn->reconnect.retries = 0;
+ conn->reconnect.previous = now;
+ }
+
+ /* are we reconnectind too often and too fast? */
+ if (conn->reconnect.retries > conn->reconnect.max_retries) return;
+
+ /* keep track of the number of reconnections */
+ conn->reconnect.retries++;
+
+ /* reconnect */
+ status = ldap_connect(conn, conn->reconnect.url);
+ if ( ! NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ /* rebind */
+ status = ldap_rebind(conn);
+ if ( ! NT_STATUS_IS_OK(status)) {
+ ldap_connection_dead(conn);
+ }
+}
+
+/* destroy an open ldap request */
+static int ldap_request_destructor(struct ldap_request *req)
+{
+ if (req->state == LDAP_REQUEST_PENDING) {
+ DLIST_REMOVE(req->conn->pending, req);
+ }
+ return 0;
+}
+
+/*
+ called on timeout of a ldap request
+*/
+static void ldap_request_timeout(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct ldap_request *req = talloc_get_type(private_data, struct ldap_request);
+ req->status = NT_STATUS_IO_TIMEOUT;
+ if (req->state == LDAP_REQUEST_PENDING) {
+ DLIST_REMOVE(req->conn->pending, req);
+ }
+ req->state = LDAP_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+
+/*
+ called on completion of a one-way ldap request
+*/
+static void ldap_request_complete(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct ldap_request *req = talloc_get_type(private_data, struct ldap_request);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+/*
+ send a ldap message - async interface
+*/
+_PUBLIC_ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
+ struct ldap_message *msg)
+{
+ struct ldap_request *req;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ req = talloc_zero(conn, struct ldap_request);
+ if (req == NULL) return NULL;
+
+ if (conn->sock == NULL) {
+ status = NT_STATUS_INVALID_CONNECTION;
+ goto failed;
+ }
+
+ req->state = LDAP_REQUEST_SEND;
+ req->conn = conn;
+ req->messageid = conn->next_messageid++;
+ if (conn->next_messageid == 0) {
+ conn->next_messageid = 1;
+ }
+ req->type = msg->type;
+ if (req->messageid == -1) {
+ goto failed;
+ }
+
+ talloc_set_destructor(req, ldap_request_destructor);
+
+ msg->messageid = req->messageid;
+
+ if (!ldap_encode(msg, samba_ldap_control_handlers(), &req->data, req)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto failed;
+ }
+
+ status = packet_send(conn->packet, req->data);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* some requests don't expect a reply, so don't add those to the
+ pending queue */
+ if (req->type == LDAP_TAG_AbandonRequest ||
+ req->type == LDAP_TAG_UnbindRequest) {
+ req->status = NT_STATUS_OK;
+ req->state = LDAP_REQUEST_DONE;
+ /* we can't call the async callback now, as it isn't setup, so
+ call it as next event */
+ tevent_add_timer(conn->event.event_ctx, req, timeval_zero(),
+ ldap_request_complete, req);
+ return req;
+ }
+
+ req->state = LDAP_REQUEST_PENDING;
+ DLIST_ADD(conn->pending, req);
+
+ /* put a timeout on the request */
+ req->time_event = tevent_add_timer(conn->event.event_ctx, req,
+ timeval_current_ofs(conn->timeout, 0),
+ ldap_request_timeout, req);
+
+ return req;
+
+failed:
+ req->status = status;
+ req->state = LDAP_REQUEST_ERROR;
+ tevent_add_timer(conn->event.event_ctx, req, timeval_zero(),
+ ldap_request_complete, req);
+
+ return req;
+}
+
+
+/*
+ wait for a request to complete
+ note that this does not destroy the request
+*/
+_PUBLIC_ NTSTATUS ldap_request_wait(struct ldap_request *req)
+{
+ while (req->state < LDAP_REQUEST_DONE) {
+ if (tevent_loop_once(req->conn->event.event_ctx) != 0) {
+ req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ break;
+ }
+ }
+ return req->status;
+}
+
+
+/*
+ a mapping of ldap response code to strings
+*/
+static const struct {
+ enum ldap_result_code code;
+ const char *str;
+} ldap_code_map[] = {
+#define _LDAP_MAP_CODE(c) { c, #c }
+ _LDAP_MAP_CODE(LDAP_SUCCESS),
+ _LDAP_MAP_CODE(LDAP_OPERATIONS_ERROR),
+ _LDAP_MAP_CODE(LDAP_PROTOCOL_ERROR),
+ _LDAP_MAP_CODE(LDAP_TIME_LIMIT_EXCEEDED),
+ _LDAP_MAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED),
+ _LDAP_MAP_CODE(LDAP_COMPARE_FALSE),
+ _LDAP_MAP_CODE(LDAP_COMPARE_TRUE),
+ _LDAP_MAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED),
+ _LDAP_MAP_CODE(LDAP_STRONG_AUTH_REQUIRED),
+ _LDAP_MAP_CODE(LDAP_REFERRAL),
+ _LDAP_MAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED),
+ _LDAP_MAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION),
+ _LDAP_MAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED),
+ _LDAP_MAP_CODE(LDAP_SASL_BIND_IN_PROGRESS),
+ _LDAP_MAP_CODE(LDAP_NO_SUCH_ATTRIBUTE),
+ _LDAP_MAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE),
+ _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_MATCHING),
+ _LDAP_MAP_CODE(LDAP_CONSTRAINT_VIOLATION),
+ _LDAP_MAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS),
+ _LDAP_MAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX),
+ _LDAP_MAP_CODE(LDAP_NO_SUCH_OBJECT),
+ _LDAP_MAP_CODE(LDAP_ALIAS_PROBLEM),
+ _LDAP_MAP_CODE(LDAP_INVALID_DN_SYNTAX),
+ _LDAP_MAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM),
+ _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION),
+ _LDAP_MAP_CODE(LDAP_INVALID_CREDENTIALS),
+ _LDAP_MAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTS),
+ _LDAP_MAP_CODE(LDAP_BUSY),
+ _LDAP_MAP_CODE(LDAP_UNAVAILABLE),
+ _LDAP_MAP_CODE(LDAP_UNWILLING_TO_PERFORM),
+ _LDAP_MAP_CODE(LDAP_LOOP_DETECT),
+ _LDAP_MAP_CODE(LDAP_NAMING_VIOLATION),
+ _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_VIOLATION),
+ _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF),
+ _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_RDN),
+ _LDAP_MAP_CODE(LDAP_ENTRY_ALREADY_EXISTS),
+ _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED),
+ _LDAP_MAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS),
+ _LDAP_MAP_CODE(LDAP_OTHER)
+};
+
+/*
+ used to setup the status code from a ldap response
+*/
+_PUBLIC_ NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r)
+{
+ int i;
+ const char *codename = "unknown";
+
+ if (r->resultcode == LDAP_SUCCESS) {
+ return NT_STATUS_OK;
+ }
+
+ if (conn->last_error) {
+ talloc_free(conn->last_error);
+ }
+
+ for (i=0;i<ARRAY_SIZE(ldap_code_map);i++) {
+ if (r->resultcode == ldap_code_map[i].code) {
+ codename = ldap_code_map[i].str;
+ break;
+ }
+ }
+
+ conn->last_error = talloc_asprintf(conn, "LDAP error %u %s - %s <%s> <%s>",
+ r->resultcode,
+ codename,
+ r->dn?r->dn:"(NULL)",
+ r->errormessage?r->errormessage:"",
+ r->referral?r->referral:"");
+
+ return NT_STATUS_LDAP(r->resultcode);
+}
+
+/*
+ return error string representing the last error
+*/
+_PUBLIC_ const char *ldap_errstr(struct ldap_connection *conn,
+ TALLOC_CTX *mem_ctx,
+ NTSTATUS status)
+{
+ if (NT_STATUS_IS_LDAP(status) && conn->last_error != NULL) {
+ return talloc_strdup(mem_ctx, conn->last_error);
+ }
+ return talloc_asprintf(mem_ctx, "LDAP client internal error: %s", nt_errstr(status));
+}
+
+
+/*
+ return the Nth result message, waiting if necessary
+*/
+_PUBLIC_ NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg)
+{
+ *msg = NULL;
+
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ while (req->state < LDAP_REQUEST_DONE && n >= req->num_replies) {
+ if (tevent_loop_once(req->conn->event.event_ctx) != 0) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ }
+
+ if (n < req->num_replies) {
+ *msg = req->replies[n];
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(req->status)) {
+ return req->status;
+ }
+
+ return NT_STATUS_NO_MORE_ENTRIES;
+}
+
+
+/*
+ return a single result message, checking if it is of the expected LDAP type
+*/
+_PUBLIC_ NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type)
+{
+ NTSTATUS status;
+ status = ldap_result_n(req, 0, msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if ((*msg)->type != type) {
+ *msg = NULL;
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ return status;
+}
+
+/*
+ a simple ldap transaction, for single result requests that only need a status code
+ this relies on single valued requests having the response type == request type + 1
+*/
+_PUBLIC_ NTSTATUS ldap_transaction(struct ldap_connection *conn, struct ldap_message *msg)
+{
+ struct ldap_request *req = ldap_request_send(conn, msg);
+ struct ldap_message *res;
+ NTSTATUS status;
+ status = ldap_result_n(req, 0, &res);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return status;
+ }
+ if (res->type != msg->type + 1) {
+ talloc_free(req);
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
+ status = ldap_check_response(conn, &res->r.GeneralResult);
+ talloc_free(req);
+ return status;
+}
diff --git a/source4/libcli/ldap/ldap_client.h b/source4/libcli/ldap/ldap_client.h
new file mode 100644
index 0000000000..084de2e6dc
--- /dev/null
+++ b/source4/libcli/ldap/ldap_client.h
@@ -0,0 +1,140 @@
+/*
+ Unix SMB/CIFS Implementation.
+
+ ldap client side header
+
+ Copyright (C) Andrew Tridgell 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/ldap/ldap.h"
+
+enum ldap_request_state { LDAP_REQUEST_SEND=1, LDAP_REQUEST_PENDING=2, LDAP_REQUEST_DONE=3, LDAP_REQUEST_ERROR=4 };
+
+/* this is the handle that the caller gets when an async ldap message
+ is sent */
+struct ldap_request {
+ struct ldap_request *next, *prev;
+ struct ldap_connection *conn;
+
+ enum ldap_request_tag type;
+ int messageid;
+ enum ldap_request_state state;
+
+ int num_replies;
+ struct ldap_message **replies;
+
+ NTSTATUS status;
+ DATA_BLOB data;
+ struct {
+ void (*fn)(struct ldap_request *);
+ void *private_data;
+ } async;
+
+ struct tevent_timer *time_event;
+};
+
+
+/* main context for a ldap client connection */
+struct ldap_connection {
+ struct socket_context *sock;
+ struct loadparm_context *lp_ctx;
+
+ char *host;
+ uint16_t port;
+ bool ldaps;
+
+ const char *auth_dn;
+ const char *simple_pw;
+
+ struct {
+ char *url;
+ int max_retries;
+ int retries;
+ time_t previous;
+ } reconnect;
+
+ struct {
+ enum { LDAP_BIND_SIMPLE, LDAP_BIND_SASL } type;
+ void *creds;
+ } bind;
+
+ /* next message id to assign */
+ unsigned next_messageid;
+
+ /* Outstanding LDAP requests that have not yet been replied to */
+ struct ldap_request *pending;
+
+ /* Let's support SASL */
+ struct gensec_security *gensec;
+
+ /* the default timeout for messages */
+ int timeout;
+
+ /* last error message */
+ char *last_error;
+
+ struct {
+ struct tevent_context *event_ctx;
+ struct tevent_fd *fde;
+ } event;
+
+ struct packet_context *packet;
+};
+
+struct ldap_connection *ldap4_new_connection(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev);
+
+NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url);
+struct composite_context *ldap_connect_send(struct ldap_connection *conn,
+ const char *url);
+
+NTSTATUS ldap_rebind(struct ldap_connection *conn);
+NTSTATUS ldap_bind_simple(struct ldap_connection *conn,
+ const char *userdn, const char *password);
+NTSTATUS ldap_bind_sasl(struct ldap_connection *conn,
+ struct cli_credentials *creds,
+ struct loadparm_context *lp_ctx);
+struct ldap_request *ldap_request_send(struct ldap_connection *conn,
+ struct ldap_message *msg);
+NTSTATUS ldap_request_wait(struct ldap_request *req);
+struct composite_context;
+NTSTATUS ldap_connect_recv(struct composite_context *ctx);
+NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg);
+NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type);
+NTSTATUS ldap_transaction(struct ldap_connection *conn, struct ldap_message *msg);
+const char *ldap_errstr(struct ldap_connection *conn,
+ TALLOC_CTX *mem_ctx,
+ NTSTATUS status);
+NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r);
+void ldap_set_reconn_params(struct ldap_connection *conn, int max_retries);
+int ildap_count_entries(struct ldap_connection *conn, struct ldap_message **res);
+NTSTATUS ildap_search_bytree(struct ldap_connection *conn, const char *basedn,
+ int scope, struct ldb_parse_tree *tree,
+ const char * const *attrs, bool attributesonly,
+ struct ldb_control **control_req,
+ struct ldb_control ***control_res,
+ struct ldap_message ***results);
+NTSTATUS ildap_search(struct ldap_connection *conn, const char *basedn,
+ int scope, const char *expression,
+ const char * const *attrs, bool attributesonly,
+ struct ldb_control **control_req,
+ struct ldb_control ***control_res,
+ struct ldap_message ***results);
+
+
+
diff --git a/source4/libcli/ldap/ldap_controls.c b/source4/libcli/ldap/ldap_controls.c
new file mode 100644
index 0000000000..7949758a80
--- /dev/null
+++ b/source4/libcli/ldap/ldap_controls.c
@@ -0,0 +1,1237 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+
+ Copyright (C) Simo Sorce 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 "includes.h"
+#include "../lib/util/asn1.h"
+#include "libcli/ldap/ldap.h"
+#include "lib/ldb/include/ldb.h"
+#include "libcli/ldap/ldap_proto.h"
+#include "dsdb/samdb/samdb.h"
+
+static bool decode_server_sort_response(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB attr;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_sort_resp_control *lsrc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lsrc = talloc(mem_ctx, struct ldb_sort_resp_control);
+ if (!lsrc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_enumerated(data, &(lsrc->result))) {
+ return false;
+ }
+
+ lsrc->attr_desc = NULL;
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+ if (!asn1_read_OctetString(data, mem_ctx, &attr)) {
+ return false;
+ }
+ lsrc->attr_desc = talloc_strndup(lsrc, (const char *)attr.data, attr.length);
+ if (!lsrc->attr_desc) {
+ return false;
+ }
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lsrc;
+
+ return true;
+}
+
+static bool decode_server_sort_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB attr;
+ DATA_BLOB rule;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_server_sort_control **lssc;
+ int num;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ lssc = NULL;
+
+ for (num = 0; asn1_peek_tag(data, ASN1_SEQUENCE(0)); num++) {
+ lssc = talloc_realloc(mem_ctx, lssc, struct ldb_server_sort_control *, num + 2);
+ if (!lssc) {
+ return false;
+ }
+ lssc[num] = talloc_zero(lssc, struct ldb_server_sort_control);
+ if (!lssc[num]) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &attr)) {
+ return false;
+ }
+
+ lssc[num]->attributeName = talloc_strndup(lssc[num], (const char *)attr.data, attr.length);
+ if (!lssc [num]->attributeName) {
+ return false;
+ }
+
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+ if (!asn1_read_OctetString(data, mem_ctx, &rule)) {
+ return false;
+ }
+ lssc[num]->orderingRule = talloc_strndup(lssc[num], (const char *)rule.data, rule.length);
+ if (!lssc[num]->orderingRule) {
+ return false;
+ }
+ }
+
+ if (asn1_peek_tag(data, ASN1_BOOLEAN)) {
+ bool reverse;
+ if (!asn1_read_BOOLEAN(data, &reverse)) {
+ return false;
+ }
+ lssc[num]->reverse = reverse;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+ }
+
+ if (lssc != NULL) {
+ lssc[num] = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lssc;
+
+ return true;
+}
+
+static bool decode_extended_dn_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ struct asn1_data *data;
+ struct ldb_extended_dn_control *ledc;
+
+ /* The content of this control is optional */
+ if (in.length == 0) {
+ *out = NULL;
+ return true;
+ }
+
+ data = asn1_init(mem_ctx);
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ ledc = talloc(mem_ctx, struct ldb_extended_dn_control);
+ if (!ledc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(ledc->type))) {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = ledc;
+
+ return true;
+}
+
+static bool decode_sd_flags_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_sd_flags_control *lsdfc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lsdfc = talloc(mem_ctx, struct ldb_sd_flags_control);
+ if (!lsdfc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lsdfc->secinfo_flags))) {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lsdfc;
+
+ return true;
+}
+
+static bool decode_search_options_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_search_options_control *lsoc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lsoc = talloc(mem_ctx, struct ldb_search_options_control);
+ if (!lsoc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lsoc->search_options))) {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lsoc;
+
+ return true;
+}
+
+static bool decode_paged_results_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB cookie;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_paged_control *lprc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lprc = talloc(mem_ctx, struct ldb_paged_control);
+ if (!lprc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lprc->size))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &cookie)) {
+ return false;
+ }
+ lprc->cookie_len = cookie.length;
+ if (lprc->cookie_len) {
+ lprc->cookie = talloc_memdup(lprc, cookie.data, cookie.length);
+
+ if (!(lprc->cookie)) {
+ return false;
+ }
+ } else {
+ lprc->cookie = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lprc;
+
+ return true;
+}
+
+static bool decode_dirsync_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB cookie;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_dirsync_control *ldc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ ldc = talloc(mem_ctx, struct ldb_dirsync_control);
+ if (!ldc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(ldc->flags))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(ldc->max_attributes))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &cookie)) {
+ return false;
+ }
+ ldc->cookie_len = cookie.length;
+ if (ldc->cookie_len) {
+ ldc->cookie = talloc_memdup(ldc, cookie.data, cookie.length);
+
+ if (!(ldc->cookie)) {
+ return false;
+ }
+ } else {
+ ldc->cookie = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = ldc;
+
+ return true;
+}
+
+/* seem that this controls has 2 forms one in case it is used with
+ * a Search Request and another when used ina Search Response
+ */
+static bool decode_asq_control(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB source_attribute;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_asq_control *lac;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lac = talloc(mem_ctx, struct ldb_asq_control);
+ if (!lac) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+
+ if (!asn1_read_OctetString(data, mem_ctx, &source_attribute)) {
+ return false;
+ }
+ lac->src_attr_len = source_attribute.length;
+ if (lac->src_attr_len) {
+ lac->source_attribute = talloc_strndup(lac, (const char *)source_attribute.data, source_attribute.length);
+
+ if (!(lac->source_attribute)) {
+ return false;
+ }
+ } else {
+ lac->source_attribute = NULL;
+ }
+
+ lac->request = 1;
+
+ } else if (asn1_peek_tag(data, ASN1_ENUMERATED)) {
+
+ if (!asn1_read_enumerated(data, &(lac->result))) {
+ return false;
+ }
+
+ lac->request = 0;
+
+ } else {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lac;
+
+ return true;
+}
+
+static bool decode_domain_scope_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ if (in.length != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool decode_notification_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ if (in.length != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool decode_show_deleted_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ if (in.length != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool decode_permissive_modify_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ if (in.length != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool decode_manageDSAIT_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ if (in.length != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool decode_vlv_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB assertion_value, context_id;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_vlv_req_control *lvrc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lvrc = talloc(mem_ctx, struct ldb_vlv_req_control);
+ if (!lvrc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->beforeCount))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->afterCount))) {
+ return false;
+ }
+
+ if (asn1_peek_tag(data, ASN1_CONTEXT(0))) {
+
+ lvrc->type = 0;
+
+ if (!asn1_start_tag(data, ASN1_CONTEXT(0))) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->match.byOffset.offset))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->match.byOffset.contentCount))) {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) { /*SEQUENCE*/
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) { /*CONTEXT*/
+ return false;
+ }
+
+ } else {
+
+ lvrc->type = 1;
+
+ if (!asn1_start_tag(data, ASN1_CONTEXT(1))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &assertion_value)) {
+ return false;
+ }
+ lvrc->match.gtOrEq.value_len = assertion_value.length;
+ if (lvrc->match.gtOrEq.value_len) {
+ lvrc->match.gtOrEq.value = talloc_memdup(lvrc, assertion_value.data, assertion_value.length);
+
+ if (!(lvrc->match.gtOrEq.value)) {
+ return false;
+ }
+ } else {
+ lvrc->match.gtOrEq.value = NULL;
+ }
+
+ if (!asn1_end_tag(data)) { /*CONTEXT*/
+ return false;
+ }
+ }
+
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+ if (!asn1_read_OctetString(data, mem_ctx, &context_id)) {
+ return false;
+ }
+ lvrc->ctxid_len = context_id.length;
+ if (lvrc->ctxid_len) {
+ lvrc->contextId = talloc_memdup(lvrc, context_id.data, context_id.length);
+
+ if (!(lvrc->contextId)) {
+ return false;
+ }
+ } else {
+ lvrc->contextId = NULL;
+ }
+ } else {
+ lvrc->contextId = NULL;
+ lvrc->ctxid_len = 0;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lvrc;
+
+ return true;
+}
+
+static bool decode_vlv_response(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB context_id;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_vlv_resp_control *lvrc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lvrc = talloc(mem_ctx, struct ldb_vlv_resp_control);
+ if (!lvrc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->targetPosition))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->contentCount))) {
+ return false;
+ }
+
+ if (!asn1_read_enumerated(data, &(lvrc->vlv_result))) {
+ return false;
+ }
+
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+ if (!asn1_read_OctetString(data, mem_ctx, &context_id)) {
+ return false;
+ }
+ lvrc->contextId = talloc_strndup(lvrc, (const char *)context_id.data, context_id.length);
+ if (!lvrc->contextId) {
+ return false;
+ }
+ lvrc->ctxid_len = context_id.length;
+ } else {
+ lvrc->contextId = NULL;
+ lvrc->ctxid_len = 0;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lvrc;
+
+ return true;
+}
+
+static bool encode_server_sort_response(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_sort_resp_control *lsrc = talloc_get_type(in, struct ldb_sort_resp_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_enumerated(data, lsrc->result)) {
+ return false;
+ }
+
+ if (lsrc->attr_desc) {
+ if (!asn1_write_OctetString(data, lsrc->attr_desc, strlen(lsrc->attr_desc))) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_server_sort_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_server_sort_control **lssc = talloc_get_type(in, struct ldb_server_sort_control *);
+ struct asn1_data *data = asn1_init(mem_ctx);
+ int num;
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ for (num = 0; lssc[num]; num++) {
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_OctetString(data, lssc[num]->attributeName, strlen(lssc[num]->attributeName))) {
+ return false;
+ }
+
+ if (lssc[num]->orderingRule) {
+ if (!asn1_write_OctetString(data, lssc[num]->orderingRule, strlen(lssc[num]->orderingRule))) {
+ return false;
+ }
+ }
+
+ if (lssc[num]->reverse) {
+ if (!asn1_write_BOOLEAN(data, lssc[num]->reverse)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_extended_dn_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_extended_dn_control *ledc = talloc_get_type(in, struct ldb_extended_dn_control);
+ struct asn1_data *data;
+
+ if (!in) {
+ *out = data_blob(NULL, 0);
+ return true;
+ }
+
+ data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, ledc->type)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_sd_flags_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_sd_flags_control *lsdfc = talloc_get_type(in, struct ldb_sd_flags_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lsdfc->secinfo_flags)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_search_options_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_search_options_control *lsoc = talloc_get_type(in, struct ldb_search_options_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lsoc->search_options)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_paged_results_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_paged_control *lprc = talloc_get_type(in, struct ldb_paged_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lprc->size)) {
+ return false;
+ }
+
+ if (!asn1_write_OctetString(data, lprc->cookie, lprc->cookie_len)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+/* seem that this controls has 2 forms one in case it is used with
+ * a Search Request and another when used ina Search Response
+ */
+static bool encode_asq_control(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_asq_control *lac = talloc_get_type(in, struct ldb_asq_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (lac->request) {
+
+ if (!asn1_write_OctetString(data, lac->source_attribute, lac->src_attr_len)) {
+ return false;
+ }
+ } else {
+ if (!asn1_write_enumerated(data, lac->result)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_dirsync_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_dirsync_control *ldc = talloc_get_type(in, struct ldb_dirsync_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, ldc->flags)) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, ldc->max_attributes)) {
+ return false;
+ }
+
+ if (!asn1_write_OctetString(data, ldc->cookie, ldc->cookie_len)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_domain_scope_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ if (in) {
+ return false;
+ }
+
+ *out = data_blob(NULL, 0);
+ return true;
+}
+
+static bool encode_notification_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ if (in) {
+ return false;
+ }
+
+ *out = data_blob(NULL, 0);
+ return true;
+}
+
+static bool encode_show_deleted_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ if (in) {
+ return false;
+ }
+
+ *out = data_blob(NULL, 0);
+ return true;
+}
+
+static bool encode_permissive_modify_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ if (in) {
+ return false;
+ }
+
+ *out = data_blob(NULL, 0);
+ return true;
+}
+
+static bool encode_manageDSAIT_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ if (in) {
+ return false;
+ }
+
+ *out = data_blob(NULL, 0);
+ return true;
+}
+
+static bool encode_vlv_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_vlv_req_control *lvrc = talloc_get_type(in, struct ldb_vlv_req_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->beforeCount)) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->afterCount)) {
+ return false;
+ }
+
+ if (lvrc->type == 0) {
+ if (!asn1_push_tag(data, ASN1_CONTEXT(0))) {
+ return false;
+ }
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->match.byOffset.offset)) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->match.byOffset.contentCount)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) { /*SEQUENCE*/
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) { /*CONTEXT*/
+ return false;
+ }
+ } else {
+ if (!asn1_push_tag(data, ASN1_CONTEXT(1))) {
+ return false;
+ }
+
+ if (!asn1_write_OctetString(data, lvrc->match.gtOrEq.value, lvrc->match.gtOrEq.value_len)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) { /*CONTEXT*/
+ return false;
+ }
+ }
+
+ if (lvrc->ctxid_len) {
+ if (!asn1_write_OctetString(data, lvrc->contextId, lvrc->ctxid_len)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_vlv_response(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_vlv_resp_control *lvrc = talloc_get_type(in, struct ldb_vlv_resp_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->targetPosition)) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->contentCount)) {
+ return false;
+ }
+
+ if (!asn1_write_enumerated(data, lvrc->vlv_result)) {
+ return false;
+ }
+
+ if (lvrc->ctxid_len) {
+ if (!asn1_write_OctetString(data, lvrc->contextId, lvrc->ctxid_len)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_openldap_dereference(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct dsdb_openldap_dereference_control *control = talloc_get_type(in, struct dsdb_openldap_dereference_control);
+ int i,j;
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!control) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ for (i=0; control->dereference && control->dereference[i]; i++) {
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+ if (!asn1_write_OctetString(data, control->dereference[i]->source_attribute, strlen(control->dereference[i]->source_attribute))) {
+ return false;
+ }
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+ for (j=0; control->dereference && control->dereference[i]->dereference_attribute[j]; j++) {
+ if (!asn1_write_OctetString(data, control->dereference[i]->dereference_attribute[j],
+ strlen(control->dereference[i]->dereference_attribute[j]))) {
+ return false;
+ }
+ }
+
+ asn1_pop_tag(data);
+ asn1_pop_tag(data);
+ }
+ asn1_pop_tag(data);
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+ return true;
+}
+
+static bool decode_openldap_dereference(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct dsdb_openldap_dereference_result_control *control;
+ struct dsdb_openldap_dereference_result **r = NULL;
+ int i = 0;
+ if (!data) return false;
+
+ control = talloc(mem_ctx, struct dsdb_openldap_dereference_result_control);
+ if (!control) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ control = talloc(mem_ctx, struct dsdb_openldap_dereference_result_control);
+ if (!control) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ while (asn1_tag_remaining(data) > 0) {
+ r = talloc_realloc(control, r, struct dsdb_openldap_dereference_result *, i + 2);
+ if (!r) {
+ return false;
+ }
+ r[i] = talloc_zero(r, struct dsdb_openldap_dereference_result);
+ if (!r[i]) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ asn1_read_OctetString_talloc(r[i], data, &r[i]->source_attribute);
+ asn1_read_OctetString_talloc(r[i], data, &r[i]->dereferenced_dn);
+ if (asn1_peek_tag(data, ASN1_CONTEXT(0))) {
+ if (!asn1_start_tag(data, ASN1_CONTEXT(0))) {
+ return false;
+ }
+
+ ldap_decode_attribs_bare(r, data, &r[i]->attributes,
+ &r[i]->num_attributes);
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+ }
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+ i++;
+ r[i] = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ control->attributes = r;
+ *out = control;
+
+ return true;
+}
+
+static const struct ldap_control_handler ldap_known_controls[] = {
+ { "1.2.840.113556.1.4.319", decode_paged_results_request, encode_paged_results_request },
+ { "1.2.840.113556.1.4.529", decode_extended_dn_request, encode_extended_dn_request },
+ { "1.2.840.113556.1.4.473", decode_server_sort_request, encode_server_sort_request },
+ { "1.2.840.113556.1.4.474", decode_server_sort_response, encode_server_sort_response },
+ { "1.2.840.113556.1.4.1504", decode_asq_control, encode_asq_control },
+ { "1.2.840.113556.1.4.841", decode_dirsync_request, encode_dirsync_request },
+ { "1.2.840.113556.1.4.528", decode_notification_request, encode_notification_request },
+ { "1.2.840.113556.1.4.417", decode_show_deleted_request, encode_show_deleted_request },
+ { "1.2.840.113556.1.4.1413", decode_permissive_modify_request, encode_permissive_modify_request },
+ { "1.2.840.113556.1.4.801", decode_sd_flags_request, encode_sd_flags_request },
+ { "1.2.840.113556.1.4.1339", decode_domain_scope_request, encode_domain_scope_request },
+ { "1.2.840.113556.1.4.1340", decode_search_options_request, encode_search_options_request },
+ { "2.16.840.1.113730.3.4.2", decode_manageDSAIT_request, encode_manageDSAIT_request },
+ { "2.16.840.1.113730.3.4.9", decode_vlv_request, encode_vlv_request },
+ { "2.16.840.1.113730.3.4.10", decode_vlv_response, encode_vlv_response },
+/* DSDB_CONTROL_CURRENT_PARTITION_OID is internal only, and has no network representation */
+ { "1.3.6.1.4.1.7165.4.3.2", NULL, NULL },
+/* DSDB_EXTENDED_REPLICATED_OBJECTS_OID is internal only, and has no network representation */
+ { "1.3.6.1.4.1.7165.4.4.1", NULL, NULL },
+ { DSDB_OPENLDAP_DEREFERENCE_CONTROL, decode_openldap_dereference, encode_openldap_dereference},
+ { NULL, NULL, NULL }
+};
+
+const struct ldap_control_handler *samba_ldap_control_handlers(void)
+{
+ return ldap_known_controls;
+}
+
diff --git a/source4/libcli/ldap/ldap_ildap.c b/source4/libcli/ldap/ldap_ildap.c
new file mode 100644
index 0000000000..f059e3882a
--- /dev/null
+++ b/source4/libcli/ldap/ldap_ildap.c
@@ -0,0 +1,129 @@
+/*
+ Unix SMB/CIFS mplementation.
+
+ ildap api - an api similar to the traditional ldap api
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/ldap/ldap.h"
+#include "libcli/ldap/ldap_client.h"
+
+
+/*
+ count the returned search entries
+*/
+_PUBLIC_ int ildap_count_entries(struct ldap_connection *conn, struct ldap_message **res)
+{
+ int i;
+ for (i=0;res && res[i];i++) /* noop */ ;
+ return i;
+}
+
+
+/*
+ perform a synchronous ldap search
+*/
+_PUBLIC_ NTSTATUS ildap_search_bytree(struct ldap_connection *conn, const char *basedn,
+ int scope, struct ldb_parse_tree *tree,
+ const char * const *attrs, bool attributesonly,
+ struct ldb_control **control_req,
+ struct ldb_control ***control_res,
+ struct ldap_message ***results)
+{
+ struct ldap_message *msg;
+ int n, i;
+ NTSTATUS status;
+ struct ldap_request *req;
+
+ if (control_res)
+ *control_res = NULL;
+ *results = NULL;
+
+ msg = new_ldap_message(conn);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ for (n=0;attrs && attrs[n];n++) /* noop */ ;
+
+ msg->type = LDAP_TAG_SearchRequest;
+ msg->r.SearchRequest.basedn = basedn;
+ msg->r.SearchRequest.scope = scope;
+ msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
+ msg->r.SearchRequest.timelimit = 0;
+ msg->r.SearchRequest.sizelimit = 0;
+ msg->r.SearchRequest.attributesonly = attributesonly;
+ msg->r.SearchRequest.tree = tree;
+ msg->r.SearchRequest.num_attributes = n;
+ msg->r.SearchRequest.attributes = attrs;
+ msg->controls = control_req;
+
+ req = ldap_request_send(conn, msg);
+ talloc_steal(msg, req);
+
+ for (i=n=0;true;i++) {
+ struct ldap_message *res;
+ status = ldap_result_n(req, i, &res);
+ if (!NT_STATUS_IS_OK(status)) break;
+
+ if (res->type == LDAP_TAG_SearchResultDone) {
+ status = ldap_check_response(conn, &res->r.GeneralResult);
+ if (control_res) {
+ *control_res = talloc_steal(conn, res->controls);
+ }
+ break;
+ }
+
+ if (res->type != LDAP_TAG_SearchResultEntry &&
+ res->type != LDAP_TAG_SearchResultReference)
+ continue;
+
+ (*results) = talloc_realloc(conn, *results, struct ldap_message *, n+2);
+ if (*results == NULL) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+ (*results)[n] = talloc_steal(*results, res);
+ (*results)[n+1] = NULL;
+ n++;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+ status = NT_STATUS_OK;
+ }
+
+ return status;
+}
+
+/*
+ perform a ldap search
+*/
+_PUBLIC_ NTSTATUS ildap_search(struct ldap_connection *conn, const char *basedn,
+ int scope, const char *expression,
+ const char * const *attrs, bool attributesonly,
+ struct ldb_control **control_req,
+ struct ldb_control ***control_res,
+ struct ldap_message ***results)
+{
+ struct ldb_parse_tree *tree = ldb_parse_tree(conn, expression);
+ NTSTATUS status;
+ status = ildap_search_bytree(conn, basedn, scope, tree, attrs,
+ attributesonly, control_req,
+ control_res, results);
+ talloc_free(tree);
+ return status;
+}
diff --git a/source4/libcli/libcli.h b/source4/libcli/libcli.h
new file mode 100644
index 0000000000..e729e12721
--- /dev/null
+++ b/source4/libcli/libcli.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) James Myers 2003 <myersjj@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/>.
+*/
+
+#ifndef __LIBCLI_H__
+#define __LIBCLI_H__
+
+#include "librpc/gen_ndr/nbt.h"
+
+struct substitute_context;
+
+/*
+ smbcli_state: internal state used in libcli library for single-threaded callers,
+ i.e. a single session on a single socket.
+ */
+struct smbcli_state {
+ struct smbcli_transport *transport;
+ struct smbcli_session *session;
+ struct smbcli_tree *tree;
+ struct substitute_context *substitute;
+ struct smblsa_state *lsa;
+};
+
+struct clilist_file_info {
+ uint64_t size;
+ uint16_t attrib;
+ time_t mtime;
+ const char *name;
+ const char *short_name;
+};
+
+struct nbt_dc_name {
+ const char *address;
+ const char *name;
+};
+
+struct cli_credentials;
+struct tevent_context;
+
+/* passed to br lock code. */
+enum brl_type {
+ READ_LOCK,
+ WRITE_LOCK,
+ PENDING_READ_LOCK,
+ PENDING_WRITE_LOCK
+};
+
+
+
+#include "libcli/raw/libcliraw.h"
+struct gensec_settings;
+#include "libcli/libcli_proto.h"
+
+#endif /* __LIBCLI_H__ */
diff --git a/source4/libcli/rap/rap.h b/source4/libcli/rap/rap.h
new file mode 100644
index 0000000000..6dcaa9bc83
--- /dev/null
+++ b/source4/libcli/rap/rap.h
@@ -0,0 +1,358 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAP operations
+ Copyright (C) Volker Lendecke 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/>.
+*/
+
+#define RAP_WshareEnum 0
+#define RAP_WshareGetInfo 1
+#define RAP_WshareSetInfo 2
+#define RAP_WshareAdd 3
+#define RAP_WshareDel 4
+#define RAP_NetShareCheck 5
+#define RAP_WsessionEnum 6
+#define RAP_WsessionGetInfo 7
+#define RAP_WsessionDel 8
+#define RAP_WconnectionEnum 9
+#define RAP_WfileEnum 10
+#define RAP_WfileGetInfo 11
+#define RAP_WfileClose 12
+#define RAP_WserverGetInfo 13
+#define RAP_WserverSetInfo 14
+#define RAP_WserverDiskEnum 15
+#define RAP_WserverAdminCommand 16
+#define RAP_NetAuditOpen 17
+#define RAP_WauditClear 18
+#define RAP_NetErrorLogOpen 19
+#define RAP_WerrorLogClear 20
+#define RAP_NetCharDevEnum 21
+#define RAP_NetCharDevGetInfo 22
+#define RAP_WCharDevControl 23
+#define RAP_NetCharDevQEnum 24
+#define RAP_NetCharDevQGetInfo 25
+#define RAP_WCharDevQSetInfo 26
+#define RAP_WCharDevQPurge 27
+#define RAP_WCharDevQPurgeSelf 28
+#define RAP_WMessageNameEnum 29
+#define RAP_WMessageNameGetInfo 30
+#define RAP_WMessageNameAdd 31
+#define RAP_WMessageNameDel 32
+#define RAP_WMessageNameFwd 33
+#define RAP_WMessageNameUnFwd 34
+#define RAP_WMessageBufferSend 35
+#define RAP_WMessageFileSend 36
+#define RAP_WMessageLogFileSet 37
+#define RAP_WMessageLogFileGet 38
+#define RAP_WServiceEnum 39
+#define RAP_WServiceInstall 40
+#define RAP_WServiceControl 41
+#define RAP_WAccessEnum 42
+#define RAP_WAccessGetInfo 43
+#define RAP_WAccessSetInfo 44
+#define RAP_WAccessAdd 45
+#define RAP_WAccessDel 46
+#define RAP_WGroupEnum 47
+#define RAP_WGroupAdd 48
+#define RAP_WGroupDel 49
+#define RAP_WGroupAddUser 50
+#define RAP_WGroupDelUser 51
+#define RAP_WGroupGetUsers 52
+#define RAP_WUserEnum 53
+#define RAP_WUserAdd 54
+#define RAP_WUserDel 55
+#define RAP_WUserGetInfo 56
+#define RAP_WUserSetInfo 57
+#define RAP_WUserPasswordSet 58
+#define RAP_WUserGetGroups 59
+#define RAP_WWkstaSetUID 62
+#define RAP_WWkstaGetInfo 63
+#define RAP_WWkstaSetInfo 64
+#define RAP_WUseEnum 65
+#define RAP_WUseAdd 66
+#define RAP_WUseDel 67
+#define RAP_WUseGetInfo 68
+#define RAP_WPrintQEnum 69
+#define RAP_WPrintQGetInfo 70
+#define RAP_WPrintQSetInfo 71
+#define RAP_WPrintQAdd 72
+#define RAP_WPrintQDel 73
+#define RAP_WPrintQPause 74
+#define RAP_WPrintQContinue 75
+#define RAP_WPrintJobEnum 76
+#define RAP_WPrintJobGetInfo 77
+#define RAP_WPrintJobSetInfo_OLD 78
+#define RAP_WPrintJobDel 81
+#define RAP_WPrintJobPause 82
+#define RAP_WPrintJobContinue 83
+#define RAP_WPrintDestEnum 84
+#define RAP_WPrintDestGetInfo 85
+#define RAP_WPrintDestControl 86
+#define RAP_WProfileSave 87
+#define RAP_WProfileLoad 88
+#define RAP_WStatisticsGet 89
+#define RAP_WStatisticsClear 90
+#define RAP_NetRemoteTOD 91
+#define RAP_WNetBiosEnum 92
+#define RAP_WNetBiosGetInfo 93
+#define RAP_NetServerEnum 94
+#define RAP_I_NetServerEnum 95
+#define RAP_WServiceGetInfo 96
+#define RAP_WPrintQPurge 103
+#define RAP_NetServerEnum2 104
+#define RAP_WAccessGetUserPerms 105
+#define RAP_WGroupGetInfo 106
+#define RAP_WGroupSetInfo 107
+#define RAP_WGroupSetUsers 108
+#define RAP_WUserSetGroups 109
+#define RAP_WUserModalsGet 110
+#define RAP_WUserModalsSet 111
+#define RAP_WFileEnum2 112
+#define RAP_WUserAdd2 113
+#define RAP_WUserSetInfo2 114
+#define RAP_WUserPasswordSet2 115
+#define RAP_I_NetServerEnum2 116
+#define RAP_WConfigGet2 117
+#define RAP_WConfigGetAll2 118
+#define RAP_WGetDCName 119
+#define RAP_NetHandleGetInfo 120
+#define RAP_NetHandleSetInfo 121
+#define RAP_WStatisticsGet2 122
+#define RAP_WBuildGetInfo 123
+#define RAP_WFileGetInfo2 124
+#define RAP_WFileClose2 125
+#define RAP_WNetServerReqChallenge 126
+#define RAP_WNetServerAuthenticate 127
+#define RAP_WNetServerPasswordSet 128
+#define RAP_WNetAccountDeltas 129
+#define RAP_WNetAccountSync 130
+#define RAP_WUserEnum2 131
+#define RAP_WWkstaUserLogon 132
+#define RAP_WWkstaUserLogoff 133
+#define RAP_WLogonEnum 134
+#define RAP_WErrorLogRead 135
+#define RAP_NetPathType 136
+#define RAP_NetPathCanonicalize 137
+#define RAP_NetPathCompare 138
+#define RAP_NetNameValidate 139
+#define RAP_NetNameCanonicalize 140
+#define RAP_NetNameCompare 141
+#define RAP_WAuditRead 142
+#define RAP_WPrintDestAdd 143
+#define RAP_WPrintDestSetInfo 144
+#define RAP_WPrintDestDel 145
+#define RAP_WUserValidate2 146
+#define RAP_WPrintJobSetInfo 147
+#define RAP_TI_NetServerDiskEnum 148
+#define RAP_TI_NetServerDiskGetInfo 149
+#define RAP_TI_FTVerifyMirror 150
+#define RAP_TI_FTAbortVerify 151
+#define RAP_TI_FTGetInfo 152
+#define RAP_TI_FTSetInfo 153
+#define RAP_TI_FTLockDisk 154
+#define RAP_TI_FTFixError 155
+#define RAP_TI_FTAbortFix 156
+#define RAP_TI_FTDiagnoseError 157
+#define RAP_TI_FTGetDriveStats 158
+#define RAP_TI_FTErrorGetInfo 160
+#define RAP_NetAccessCheck 163
+#define RAP_NetAlertRaise 164
+#define RAP_NetAlertStart 165
+#define RAP_NetAlertStop 166
+#define RAP_NetAuditWrite 167
+#define RAP_NetIRemoteAPI 168
+#define RAP_NetServiceStatus 169
+#define RAP_NetServerRegister 170
+#define RAP_NetServerDeregister 171
+#define RAP_NetSessionEntryMake 172
+#define RAP_NetSessionEntryClear 173
+#define RAP_NetSessionEntryGetInfo 174
+#define RAP_NetSessionEntrySetInfo 175
+#define RAP_NetConnectionEntryMake 176
+#define RAP_NetConnectionEntryClear 177
+#define RAP_NetConnectionEntrySetInfo 178
+#define RAP_NetConnectionEntryGetInfo 179
+#define RAP_NetFileEntryMake 180
+#define RAP_NetFileEntryClear 181
+#define RAP_NetFileEntrySetInfo 182
+#define RAP_NetFileEntryGetInfo 183
+#define RAP_AltSrvMessageBufferSend 184
+#define RAP_AltSrvMessageFileSend 185
+#define RAP_wI_NetRplWkstaEnum 186
+#define RAP_wI_NetRplWkstaGetInfo 187
+#define RAP_wI_NetRplWkstaSetInfo 188
+#define RAP_wI_NetRplWkstaAdd 189
+#define RAP_wI_NetRplWkstaDel 190
+#define RAP_wI_NetRplProfileEnum 191
+#define RAP_wI_NetRplProfileGetInfo 192
+#define RAP_wI_NetRplProfileSetInfo 193
+#define RAP_wI_NetRplProfileAdd 194
+#define RAP_wI_NetRplProfileDel 195
+#define RAP_wI_NetRplProfileClone 196
+#define RAP_wI_NetRplBaseProfileEnum 197
+#define RAP_WIServerSetInfo 201
+#define RAP_WPrintDriverEnum 205
+#define RAP_WPrintQProcessorEnum 206
+#define RAP_WPrintPortEnum 207
+#define RAP_WNetWriteUpdateLog 208
+#define RAP_WNetAccountUpdate 209
+#define RAP_WNetAccountConfirmUpdate 210
+#define RAP_WConfigSet 211
+#define RAP_WAccountsReplicate 212
+#define RAP_SamOEMChgPasswordUser2_P 214
+#define RAP_NetServerEnum3 215
+#define RAP_WprintDriverGetInfo 250
+#define RAP_WprintDriverSetInfo 251
+#define RAP_WaliasAdd 252
+#define RAP_WaliasDel 253
+#define RAP_WaliasGetInfo 254
+#define RAP_WaliasSetInfo 255
+#define RAP_WaliasEnum 256
+#define RAP_WuserGetLogonAsn 257
+#define RAP_WuserSetLogonAsn 258
+#define RAP_WuserGetAppSel 259
+#define RAP_WuserSetAppSel 260
+#define RAP_WappAdd 261
+#define RAP_WappDel 262
+#define RAP_WappGetInfo 263
+#define RAP_WappSetInfo 264
+#define RAP_WappEnum 265
+#define RAP_WUserDCDBInit 266
+#define RAP_WDASDAdd 267
+#define RAP_WDASDDel 268
+#define RAP_WDASDGetInfo 269
+#define RAP_WDASDSetInfo 270
+#define RAP_WDASDEnum 271
+#define RAP_WDASDCheck 272
+#define RAP_WDASDCtl 273
+#define RAP_WuserRemoteLogonCheck 274
+#define RAP_WUserPasswordSet3 275
+#define RAP_WCreateRIPLMachine 276
+#define RAP_WDeleteRIPLMachine 277
+#define RAP_WGetRIPLMachineInfo 278
+#define RAP_WSetRIPLMachineInfo 279
+#define RAP_WEnumRIPLMachine 280
+#define RAP_I_ShareAdd 281
+#define RAP_AliasEnum 282
+#define RAP_WaccessApply 283
+#define RAP_WPrt16Query 284
+#define RAP_WPrt16Set 285
+#define RAP_WUserDel100 286
+#define RAP_WUserRemoteLogonCheck2 287
+#define RAP_WRemoteTODSet 294
+#define RAP_WprintJobMoveAll 295
+#define RAP_W16AppParmAdd 296
+#define RAP_W16AppParmDel 297
+#define RAP_W16AppParmGet 298
+#define RAP_W16AppParmSet 299
+#define RAP_W16RIPLMachineCreate 300
+#define RAP_W16RIPLMachineGetInfo 301
+#define RAP_W16RIPLMachineSetInfo 302
+#define RAP_W16RIPLMachineEnum 303
+#define RAP_W16RIPLMachineListParmEnum 304
+#define RAP_W16RIPLMachClassGetInfo 305
+#define RAP_W16RIPLMachClassEnum 306
+#define RAP_W16RIPLMachClassCreate 307
+#define RAP_W16RIPLMachClassSetInfo 308
+#define RAP_W16RIPLMachClassDelete 309
+#define RAP_W16RIPLMachClassLPEnum 310
+#define RAP_W16RIPLMachineDelete 311
+#define RAP_W16WSLevelGetInfo 312
+#define RAP_WserverNameAdd 313
+#define RAP_WserverNameDel 314
+#define RAP_WserverNameEnum 315
+#define RAP_I_WDASDEnum 316
+#define RAP_WDASDEnumTerminate 317
+#define RAP_WDASDSetInfo2 318
+#define MAX_API 318
+
+struct rap_shareenum_info_0 {
+ char name[13];
+};
+
+struct rap_shareenum_info_1 {
+ char name[13];
+ char pad;
+ uint16_t type;
+ char *comment;
+};
+
+union rap_shareenum_info {
+ struct rap_shareenum_info_0 info0;
+ struct rap_shareenum_info_1 info1;
+};
+
+struct rap_NetShareEnum {
+ struct {
+ uint16_t level;
+ uint16_t bufsize;
+ } in;
+
+ struct {
+ uint16_t status;
+ uint16_t convert;
+ uint16_t count;
+ uint16_t available;
+ union rap_shareenum_info *info;
+ } out;
+};
+
+struct rap_server_info_0 {
+ char name[16];
+};
+
+struct rap_server_info_1 {
+ char name[16];
+ uint8_t version_major;
+ uint8_t version_minor;
+ uint32_t servertype;
+ char *comment;
+};
+
+union rap_server_info {
+ struct rap_server_info_0 info0;
+ struct rap_server_info_1 info1;
+};
+
+struct rap_NetServerEnum2 {
+ struct {
+ uint16_t level;
+ uint16_t bufsize;
+ uint32_t servertype;
+ const char *domain;
+ } in;
+
+ struct {
+ uint16_t status;
+ uint16_t convert;
+ uint16_t count;
+ uint16_t available;
+ union rap_server_info *info;
+ } out;
+};
+
+struct rap_WserverGetInfo {
+ struct {
+ uint16_t level;
+ uint16_t bufsize;
+ } in;
+
+ struct {
+ uint16_t status;
+ uint16_t convert;
+ uint16_t available;
+ union rap_server_info info;
+ } out;
+};
diff --git a/source4/libcli/raw/README b/source4/libcli/raw/README
new file mode 100644
index 0000000000..cb3e507e3a
--- /dev/null
+++ b/source4/libcli/raw/README
@@ -0,0 +1,5 @@
+Design notes for client library restructure:
+
+1 - no references to cli_state should exist in libcli/raw.
+2 - all interfaces to functions in this directory should use cli_session or cli_tree as
+ the primary context structure \ No newline at end of file
diff --git a/source4/libcli/raw/clierror.c b/source4/libcli/raw/clierror.c
new file mode 100644
index 0000000000..a41748640b
--- /dev/null
+++ b/source4/libcli/raw/clierror.c
@@ -0,0 +1,73 @@
+/*
+ Unix SMB/CIFS implementation.
+ client error handling routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) James Myers 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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+
+/***************************************************************************
+ Return an error message from the last response
+****************************************************************************/
+_PUBLIC_ const char *smbcli_errstr(struct smbcli_tree *tree)
+{
+ switch (tree->session->transport->error.etype) {
+ case ETYPE_SMB:
+ return nt_errstr(tree->session->transport->error.e.nt_status);
+
+ case ETYPE_SOCKET:
+ return "socket_error";
+
+ case ETYPE_NBT:
+ return "nbt_error";
+
+ case ETYPE_NONE:
+ return "no_error";
+ }
+ return NULL;
+}
+
+
+/* Return the 32-bit NT status code from the last packet */
+_PUBLIC_ NTSTATUS smbcli_nt_error(struct smbcli_tree *tree)
+{
+ switch (tree->session->transport->error.etype) {
+ case ETYPE_SMB:
+ return tree->session->transport->error.e.nt_status;
+
+ case ETYPE_SOCKET:
+ return NT_STATUS_UNSUCCESSFUL;
+
+ case ETYPE_NBT:
+ return NT_STATUS_UNSUCCESSFUL;
+
+ case ETYPE_NONE:
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+
+/* Return true if the last packet was an error */
+bool smbcli_is_error(struct smbcli_tree *tree)
+{
+ return NT_STATUS_IS_ERR(smbcli_nt_error(tree));
+}
diff --git a/source4/libcli/raw/clioplock.c b/source4/libcli/raw/clioplock.c
new file mode 100644
index 0000000000..42ac6b517b
--- /dev/null
+++ b/source4/libcli/raw/clioplock.c
@@ -0,0 +1,62 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client oplock functions
+ Copyright (C) Andrew Tridgell 2001
+
+ 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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+/****************************************************************************
+send an ack for an oplock break request
+****************************************************************************/
+_PUBLIC_ bool smbcli_oplock_ack(struct smbcli_tree *tree, uint16_t fnum, uint16_t ack_level)
+{
+ bool ret;
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBlockingX, 8, 0);
+
+ SSVAL(req->out.vwv,VWV(0),0xFF);
+ SSVAL(req->out.vwv,VWV(1),0);
+ SSVAL(req->out.vwv,VWV(2),fnum);
+ SCVAL(req->out.vwv,VWV(3),LOCKING_ANDX_OPLOCK_RELEASE);
+ SCVAL(req->out.vwv,VWV(3)+1,ack_level);
+ SIVAL(req->out.vwv,VWV(4),0);
+ SSVAL(req->out.vwv,VWV(6),0);
+ SSVAL(req->out.vwv,VWV(7),0);
+
+ /* this request does not expect a reply, so tell the signing
+ subsystem not to allocate an id for a reply */
+ req->one_way_request = 1;
+
+ ret = smbcli_request_send(req);
+
+ return ret;
+}
+
+
+/****************************************************************************
+set the oplock handler for a connection
+****************************************************************************/
+_PUBLIC_ void smbcli_oplock_handler(struct smbcli_transport *transport,
+ bool (*handler)(struct smbcli_transport *, uint16_t, uint16_t, uint8_t, void *),
+ void *private_data)
+{
+ transport->oplock.handler = handler;
+ transport->oplock.private_data = private_data;
+}
diff --git a/source4/libcli/raw/clisession.c b/source4/libcli/raw/clisession.c
new file mode 100644
index 0000000000..41765bfb2b
--- /dev/null
+++ b/source4/libcli/raw/clisession.c
@@ -0,0 +1,297 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client session context management functions
+
+ Copyright (C) Andrew Tridgell 1994-2005
+ Copyright (C) James Myers 2003 <myersjj@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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "system/filesys.h"
+
+#define SETUP_REQUEST_SESSION(cmd, wct, buflen) do { \
+ req = smbcli_request_setup_session(session, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+
+/****************************************************************************
+ Initialize the session context
+****************************************************************************/
+struct smbcli_session *smbcli_session_init(struct smbcli_transport *transport,
+ TALLOC_CTX *parent_ctx, bool primary,
+ struct smbcli_session_options options)
+{
+ struct smbcli_session *session;
+ uint16_t flags2;
+ uint32_t capabilities;
+
+ session = talloc_zero(parent_ctx, struct smbcli_session);
+ if (!session) {
+ return NULL;
+ }
+
+ if (primary) {
+ session->transport = talloc_steal(session, transport);
+ } else {
+ session->transport = talloc_reference(session, transport);
+ }
+ session->pid = (uint16_t)getpid();
+ session->vuid = UID_FIELD_INVALID;
+ session->options = options;
+
+ capabilities = transport->negotiate.capabilities;
+
+ flags2 = FLAGS2_LONG_PATH_COMPONENTS | FLAGS2_EXTENDED_ATTRIBUTES;
+
+ if (capabilities & CAP_UNICODE) {
+ flags2 |= FLAGS2_UNICODE_STRINGS;
+ }
+ if (capabilities & CAP_STATUS32) {
+ flags2 |= FLAGS2_32_BIT_ERROR_CODES;
+ }
+ if (capabilities & CAP_EXTENDED_SECURITY) {
+ flags2 |= FLAGS2_EXTENDED_SECURITY;
+ }
+ if (session->transport->negotiate.sign_info.doing_signing) {
+ flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+ }
+
+ session->flags2 = flags2;
+
+ return session;
+}
+
+/****************************************************************************
+ Perform a session setup (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_sesssetup_send(struct smbcli_session *session,
+ union smb_sesssetup *parms)
+{
+ struct smbcli_request *req = NULL;
+
+ switch (parms->old.level) {
+ case RAW_SESSSETUP_OLD:
+ SETUP_REQUEST_SESSION(SMBsesssetupX, 10, 0);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv,VWV(2),parms->old.in.bufsize);
+ SSVAL(req->out.vwv,VWV(3),parms->old.in.mpx_max);
+ SSVAL(req->out.vwv,VWV(4),parms->old.in.vc_num);
+ SIVAL(req->out.vwv,VWV(5),parms->old.in.sesskey);
+ SSVAL(req->out.vwv,VWV(7),parms->old.in.password.length);
+ SIVAL(req->out.vwv,VWV(8), 0); /* reserved */
+ smbcli_req_append_blob(req, &parms->old.in.password);
+ smbcli_req_append_string(req, parms->old.in.user, STR_TERMINATE);
+ smbcli_req_append_string(req, parms->old.in.domain, STR_TERMINATE|STR_UPPER);
+ smbcli_req_append_string(req, parms->old.in.os, STR_TERMINATE);
+ smbcli_req_append_string(req, parms->old.in.lanman, STR_TERMINATE);
+ break;
+
+ case RAW_SESSSETUP_NT1:
+ SETUP_REQUEST_SESSION(SMBsesssetupX, 13, 0);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->nt1.in.bufsize);
+ SSVAL(req->out.vwv, VWV(3), parms->nt1.in.mpx_max);
+ SSVAL(req->out.vwv, VWV(4), parms->nt1.in.vc_num);
+ SIVAL(req->out.vwv, VWV(5), parms->nt1.in.sesskey);
+ SSVAL(req->out.vwv, VWV(7), parms->nt1.in.password1.length);
+ SSVAL(req->out.vwv, VWV(8), parms->nt1.in.password2.length);
+ SIVAL(req->out.vwv, VWV(9), 0); /* reserved */
+ SIVAL(req->out.vwv, VWV(11), parms->nt1.in.capabilities);
+ smbcli_req_append_blob(req, &parms->nt1.in.password1);
+ smbcli_req_append_blob(req, &parms->nt1.in.password2);
+ smbcli_req_append_string(req, parms->nt1.in.user, STR_TERMINATE);
+ smbcli_req_append_string(req, parms->nt1.in.domain, STR_TERMINATE|STR_UPPER);
+ smbcli_req_append_string(req, parms->nt1.in.os, STR_TERMINATE);
+ smbcli_req_append_string(req, parms->nt1.in.lanman, STR_TERMINATE);
+ break;
+
+ case RAW_SESSSETUP_SPNEGO:
+ SETUP_REQUEST_SESSION(SMBsesssetupX, 12, 0);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->spnego.in.bufsize);
+ SSVAL(req->out.vwv, VWV(3), parms->spnego.in.mpx_max);
+ SSVAL(req->out.vwv, VWV(4), parms->spnego.in.vc_num);
+ SIVAL(req->out.vwv, VWV(5), parms->spnego.in.sesskey);
+ SSVAL(req->out.vwv, VWV(7), parms->spnego.in.secblob.length);
+ SIVAL(req->out.vwv, VWV(8), 0); /* reserved */
+ SIVAL(req->out.vwv, VWV(10), parms->spnego.in.capabilities);
+ smbcli_req_append_blob(req, &parms->spnego.in.secblob);
+ smbcli_req_append_string(req, parms->spnego.in.os, STR_TERMINATE);
+ smbcli_req_append_string(req, parms->spnego.in.lanman, STR_TERMINATE);
+ smbcli_req_append_string(req, parms->spnego.in.workgroup, STR_TERMINATE);
+ break;
+
+ case RAW_SESSSETUP_SMB2:
+ return NULL;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ Perform a session setup (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_sesssetup_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_sesssetup *parms)
+{
+ uint16_t len;
+ uint8_t *p;
+
+ if (!smbcli_request_receive(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ if (!NT_STATUS_IS_OK(req->status) &&
+ !NT_STATUS_EQUAL(req->status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ return smbcli_request_destroy(req);
+ }
+
+ switch (parms->old.level) {
+ case RAW_SESSSETUP_OLD:
+ SMBCLI_CHECK_WCT(req, 3);
+ ZERO_STRUCT(parms->old.out);
+ parms->old.out.vuid = SVAL(req->in.hdr, HDR_UID);
+ parms->old.out.action = SVAL(req->in.vwv, VWV(2));
+ p = req->in.data;
+ if (p) {
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->old.out.os, p, -1, STR_TERMINATE);
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->old.out.lanman, p, -1, STR_TERMINATE);
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->old.out.domain, p, -1, STR_TERMINATE);
+ }
+ break;
+
+ case RAW_SESSSETUP_NT1:
+ SMBCLI_CHECK_WCT(req, 3);
+ ZERO_STRUCT(parms->nt1.out);
+ parms->nt1.out.vuid = SVAL(req->in.hdr, HDR_UID);
+ parms->nt1.out.action = SVAL(req->in.vwv, VWV(2));
+ p = req->in.data;
+ if (p) {
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->nt1.out.os, p, -1, STR_TERMINATE);
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->nt1.out.lanman, p, -1, STR_TERMINATE);
+ if (p < (req->in.data + req->in.data_size)) {
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->nt1.out.domain, p, -1, STR_TERMINATE);
+ }
+ }
+ break;
+
+ case RAW_SESSSETUP_SPNEGO:
+ SMBCLI_CHECK_WCT(req, 4);
+ ZERO_STRUCT(parms->spnego.out);
+ parms->spnego.out.vuid = SVAL(req->in.hdr, HDR_UID);
+ parms->spnego.out.action = SVAL(req->in.vwv, VWV(2));
+ len = SVAL(req->in.vwv, VWV(3));
+ p = req->in.data;
+ if (!p) {
+ break;
+ }
+
+ parms->spnego.out.secblob = smbcli_req_pull_blob(&req->in.bufinfo, mem_ctx, p, len);
+ p += parms->spnego.out.secblob.length;
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->spnego.out.os, p, -1, STR_TERMINATE);
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->spnego.out.lanman, p, -1, STR_TERMINATE);
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->spnego.out.workgroup, p, -1, STR_TERMINATE);
+ break;
+
+ case RAW_SESSSETUP_SMB2:
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+
+/*
+ Perform a session setup (sync interface)
+*/
+NTSTATUS smb_raw_sesssetup(struct smbcli_session *session,
+ TALLOC_CTX *mem_ctx, union smb_sesssetup *parms)
+{
+ struct smbcli_request *req = smb_raw_sesssetup_send(session, parms);
+ return smb_raw_sesssetup_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ Send a ulogoff (async send)
+*****************************************************************************/
+struct smbcli_request *smb_raw_ulogoff_send(struct smbcli_session *session)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST_SESSION(SMBulogoffX, 2, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Send a ulogoff (sync interface)
+*****************************************************************************/
+NTSTATUS smb_raw_ulogoff(struct smbcli_session *session)
+{
+ struct smbcli_request *req = smb_raw_ulogoff_send(session);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Send a exit (async send)
+*****************************************************************************/
+struct smbcli_request *smb_raw_exit_send(struct smbcli_session *session)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST_SESSION(SMBexit, 0, 0);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Send a exit (sync interface)
+*****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_exit(struct smbcli_session *session)
+{
+ struct smbcli_request *req = smb_raw_exit_send(session);
+ return smbcli_request_simple_recv(req);
+}
diff --git a/source4/libcli/raw/clisocket.c b/source4/libcli/raw/clisocket.c
new file mode 100644
index 0000000000..b9e83218dd
--- /dev/null
+++ b/source4/libcli/raw/clisocket.c
@@ -0,0 +1,249 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB client socket context management functions
+
+ Copyright (C) Andrew Tridgell 1994-2005
+ Copyright (C) James Myers 2003 <myersjj@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 "includes.h"
+#include "lib/events/events.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/composite/composite.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+#include "libcli/raw/raw_proto.h"
+
+struct sock_connect_state {
+ struct composite_context *ctx;
+ const char *host_name;
+ int num_ports;
+ uint16_t *ports;
+ const char *socket_options;
+ struct smbcli_socket *result;
+};
+
+/*
+ connect a smbcli_socket context to an IP/port pair
+ if port is 0 then choose 445 then 139
+*/
+
+static void smbcli_sock_connect_recv_conn(struct composite_context *ctx);
+
+struct composite_context *smbcli_sock_connect_send(TALLOC_CTX *mem_ctx,
+ const char *host_addr,
+ const char **ports,
+ const char *host_name,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ const char *socket_options)
+{
+ struct composite_context *result, *ctx;
+ struct sock_connect_state *state;
+ int i;
+
+ result = talloc_zero(mem_ctx, struct composite_context);
+ if (result == NULL) goto failed;
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+
+ result->event_ctx = talloc_reference(result, event_ctx);
+ if (result->event_ctx == NULL) goto failed;
+
+ state = talloc(result, struct sock_connect_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->host_name = talloc_strdup(state, host_name);
+ if (state->host_name == NULL) goto failed;
+
+ state->num_ports = str_list_length(ports);
+ state->ports = talloc_array(state, uint16_t, state->num_ports);
+ if (state->ports == NULL) goto failed;
+ for (i=0;ports[i];i++) {
+ state->ports[i] = atoi(ports[i]);
+ }
+ state->socket_options = talloc_reference(state, socket_options);
+
+ ctx = socket_connect_multi_send(state, host_addr,
+ state->num_ports, state->ports,
+ resolve_ctx,
+ state->ctx->event_ctx);
+ if (ctx == NULL) goto failed;
+ ctx->async.fn = smbcli_sock_connect_recv_conn;
+ ctx->async.private_data = state;
+ return result;
+
+failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void smbcli_sock_connect_recv_conn(struct composite_context *ctx)
+{
+ struct sock_connect_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct sock_connect_state);
+ struct socket_context *sock;
+ uint16_t port;
+
+ state->ctx->status = socket_connect_multi_recv(ctx, state, &sock,
+ &port);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->ctx->status =
+ socket_set_option(sock, state->socket_options, NULL);
+ if (!composite_is_ok(state->ctx)) return;
+
+
+ state->result = talloc_zero(state, struct smbcli_socket);
+ if (composite_nomem(state->result, state->ctx)) return;
+
+ state->result->sock = talloc_steal(state->result, sock);
+ state->result->port = port;
+ state->result->hostname = talloc_steal(sock, state->host_name);
+
+ state->result->event.ctx =
+ talloc_reference(state->result, state->ctx->event_ctx);
+ if (composite_nomem(state->result->event.ctx, state->ctx)) return;
+
+ composite_done(state->ctx);
+}
+
+/*
+ finish a smbcli_sock_connect_send() operation
+*/
+NTSTATUS smbcli_sock_connect_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct smbcli_socket **result)
+{
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ struct sock_connect_state *state =
+ talloc_get_type(c->private_data,
+ struct sock_connect_state);
+ *result = talloc_steal(mem_ctx, state->result);
+ }
+ talloc_free(c);
+ return status;
+}
+
+/*
+ connect a smbcli_socket context to an IP/port pair
+ if port is 0 then choose the ports listed in smb.conf (normally 445 then 139)
+
+ sync version of the function
+*/
+NTSTATUS smbcli_sock_connect(TALLOC_CTX *mem_ctx,
+ const char *host_addr, const char **ports,
+ const char *host_name,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ const char *socket_options,
+ struct smbcli_socket **result)
+{
+ struct composite_context *c =
+ smbcli_sock_connect_send(mem_ctx, host_addr, ports, host_name,
+ resolve_ctx,
+ event_ctx, socket_options);
+ return smbcli_sock_connect_recv(c, mem_ctx, result);
+}
+
+
+/****************************************************************************
+ mark the socket as dead
+****************************************************************************/
+_PUBLIC_ void smbcli_sock_dead(struct smbcli_socket *sock)
+{
+ talloc_free(sock->event.fde);
+ sock->event.fde = NULL;
+ talloc_free(sock->sock);
+ sock->sock = NULL;
+}
+
+/****************************************************************************
+ Set socket options on a open connection.
+****************************************************************************/
+void smbcli_sock_set_options(struct smbcli_socket *sock, const char *options)
+{
+ socket_set_option(sock->sock, options, NULL);
+}
+
+/****************************************************************************
+resolve a hostname and connect
+****************************************************************************/
+_PUBLIC_ struct smbcli_socket *smbcli_sock_connect_byname(const char *host, const char **ports,
+ TALLOC_CTX *mem_ctx,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ const char *socket_options)
+{
+ int name_type = NBT_NAME_SERVER;
+ const char *address;
+ NTSTATUS status;
+ struct nbt_name nbt_name;
+ char *name, *p;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ struct smbcli_socket *result;
+
+ if (event_ctx == NULL) {
+ DEBUG(0, ("Invalid NULL event context passed in as parameter\n"));
+ return NULL;
+ }
+
+ if (tmp_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return NULL;
+ }
+
+ name = talloc_strdup(tmp_ctx, host);
+ if (name == NULL) {
+ DEBUG(0, ("talloc_strdup failed\n"));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ /* allow hostnames of the form NAME#xx and do a netbios lookup */
+ if ((p = strchr(name, '#'))) {
+ name_type = strtol(p+1, NULL, 16);
+ *p = 0;
+ }
+
+ make_nbt_name(&nbt_name, host, name_type);
+
+ status = resolve_name(resolve_ctx, &nbt_name, tmp_ctx, &address, event_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ status = smbcli_sock_connect(mem_ctx, address, ports, name, resolve_ctx,
+ event_ctx,
+ socket_options, &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(9, ("smbcli_sock_connect failed: %s\n",
+ nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return result;
+}
diff --git a/source4/libcli/raw/clitransport.c b/source4/libcli/raw/clitransport.c
new file mode 100644
index 0000000000..5cf0272e88
--- /dev/null
+++ b/source4/libcli/raw/clitransport.c
@@ -0,0 +1,680 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client transport context management functions
+
+ Copyright (C) Andrew Tridgell 1994-2005
+ Copyright (C) James Myers 2003 <myersjj@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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "lib/socket/socket.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "lib/stream/packet.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "../libcli/nbt/libnbt.h"
+
+
+/*
+ an event has happened on the socket
+*/
+static void smbcli_transport_event_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct smbcli_transport *transport = talloc_get_type(private_data,
+ struct smbcli_transport);
+ if (flags & EVENT_FD_READ) {
+ packet_recv(transport->packet);
+ return;
+ }
+ if (flags & EVENT_FD_WRITE) {
+ packet_queue_run(transport->packet);
+ }
+}
+
+/*
+ destroy a transport
+ */
+static int transport_destructor(struct smbcli_transport *transport)
+{
+ smbcli_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
+ return 0;
+}
+
+
+/*
+ handle receive errors
+*/
+static void smbcli_transport_error(void *private_data, NTSTATUS status)
+{
+ struct smbcli_transport *transport = talloc_get_type(private_data, struct smbcli_transport);
+ smbcli_transport_dead(transport, status);
+}
+
+static NTSTATUS smbcli_transport_finish_recv(void *private_data, DATA_BLOB blob);
+
+/*
+ create a transport structure based on an established socket
+*/
+struct smbcli_transport *smbcli_transport_init(struct smbcli_socket *sock,
+ TALLOC_CTX *parent_ctx,
+ bool primary,
+ struct smbcli_options *options,
+ struct smb_iconv_convenience *iconv_convenience)
+{
+ struct smbcli_transport *transport;
+
+ transport = talloc_zero(parent_ctx, struct smbcli_transport);
+ if (!transport) return NULL;
+
+ if (primary) {
+ transport->socket = talloc_steal(transport, sock);
+ } else {
+ transport->socket = talloc_reference(transport, sock);
+ }
+ transport->negotiate.protocol = PROTOCOL_NT1;
+ transport->options = *options;
+ transport->negotiate.max_xmit = transport->options.max_xmit;
+ transport->iconv_convenience = iconv_convenience;
+
+ /* setup the stream -> packet parser */
+ transport->packet = packet_init(transport);
+ if (transport->packet == NULL) {
+ talloc_free(transport);
+ return NULL;
+ }
+ packet_set_private(transport->packet, transport);
+ packet_set_socket(transport->packet, transport->socket->sock);
+ packet_set_callback(transport->packet, smbcli_transport_finish_recv);
+ packet_set_full_request(transport->packet, packet_full_request_nbt);
+ packet_set_error_handler(transport->packet, smbcli_transport_error);
+ packet_set_event_context(transport->packet, transport->socket->event.ctx);
+ packet_set_nofree(transport->packet);
+
+ smbcli_init_signing(transport);
+
+ ZERO_STRUCT(transport->called);
+
+ /* take over event handling from the socket layer - it only
+ handles events up until we are connected */
+ talloc_free(transport->socket->event.fde);
+ transport->socket->event.fde = event_add_fd(transport->socket->event.ctx,
+ transport->socket->sock,
+ socket_get_fd(transport->socket->sock),
+ EVENT_FD_READ,
+ smbcli_transport_event_handler,
+ transport);
+
+ packet_set_fde(transport->packet, transport->socket->event.fde);
+ packet_set_serialise(transport->packet);
+ talloc_set_destructor(transport, transport_destructor);
+
+ return transport;
+}
+
+/*
+ mark the transport as dead
+*/
+void smbcli_transport_dead(struct smbcli_transport *transport, NTSTATUS status)
+{
+ smbcli_sock_dead(transport->socket);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ /* kill only the first pending receive - this is so that if
+ that async function frees the connection we don't die trying
+ to use old memory. The caller has to cope with only one
+ network error */
+ if (transport->pending_recv) {
+ struct smbcli_request *req = transport->pending_recv;
+ req->state = SMBCLI_REQUEST_ERROR;
+ req->status = status;
+ DLIST_REMOVE(transport->pending_recv, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ }
+}
+
+
+/*
+ send a session request
+*/
+struct smbcli_request *smbcli_transport_connect_send(struct smbcli_transport *transport,
+ struct nbt_name *calling,
+ struct nbt_name *called)
+{
+ uint8_t *p;
+ struct smbcli_request *req;
+ DATA_BLOB calling_blob, called_blob;
+ TALLOC_CTX *tmp_ctx = talloc_new(transport);
+ NTSTATUS status;
+
+ status = nbt_name_dup(transport, called, &transport->called);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ status = nbt_name_to_blob(tmp_ctx, transport->iconv_convenience, &calling_blob, calling);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ status = nbt_name_to_blob(tmp_ctx, transport->iconv_convenience, &called_blob, called);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* allocate output buffer */
+ req = smbcli_request_setup_nonsmb(transport,
+ NBT_HDR_SIZE +
+ calling_blob.length + called_blob.length);
+ if (req == NULL) goto failed;
+
+ /* put in the destination name */
+ p = req->out.buffer + NBT_HDR_SIZE;
+ memcpy(p, called_blob.data, called_blob.length);
+ p += called_blob.length;
+
+ memcpy(p, calling_blob.data, calling_blob.length);
+ p += calling_blob.length;
+
+ _smb_setlen(req->out.buffer, PTR_DIFF(p, req->out.buffer) - NBT_HDR_SIZE);
+ SCVAL(req->out.buffer,0,0x81);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ goto failed;
+ }
+
+ talloc_free(tmp_ctx);
+ return req;
+
+failed:
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+/*
+ map a session request error to a NTSTATUS
+ */
+static NTSTATUS map_session_refused_error(uint8_t error)
+{
+ switch (error) {
+ case 0x80:
+ case 0x81:
+ return NT_STATUS_REMOTE_NOT_LISTENING;
+ case 0x82:
+ return NT_STATUS_RESOURCE_NAME_NOT_FOUND;
+ case 0x83:
+ return NT_STATUS_REMOTE_RESOURCES;
+ }
+ return NT_STATUS_UNEXPECTED_IO_ERROR;
+}
+
+
+/*
+ finish a smbcli_transport_connect()
+*/
+NTSTATUS smbcli_transport_connect_recv(struct smbcli_request *req)
+{
+ NTSTATUS status;
+
+ if (!smbcli_request_receive(req)) {
+ smbcli_request_destroy(req);
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ switch (CVAL(req->in.buffer,0)) {
+ case 0x82:
+ status = NT_STATUS_OK;
+ break;
+ case 0x83:
+ status = map_session_refused_error(CVAL(req->in.buffer,4));
+ break;
+ case 0x84:
+ DEBUG(1,("Warning: session retarget not supported\n"));
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+ default:
+ status = NT_STATUS_UNEXPECTED_IO_ERROR;
+ break;
+ }
+
+ smbcli_request_destroy(req);
+ return status;
+}
+
+
+/*
+ send a session request (if needed)
+*/
+bool smbcli_transport_connect(struct smbcli_transport *transport,
+ struct nbt_name *calling,
+ struct nbt_name *called)
+{
+ struct smbcli_request *req;
+ NTSTATUS status;
+
+ if (transport->socket->port == 445) {
+ return true;
+ }
+
+ req = smbcli_transport_connect_send(transport,
+ calling, called);
+ status = smbcli_transport_connect_recv(req);
+ return NT_STATUS_IS_OK(status);
+}
+
+/****************************************************************************
+get next mid in sequence
+****************************************************************************/
+uint16_t smbcli_transport_next_mid(struct smbcli_transport *transport)
+{
+ uint16_t mid;
+ struct smbcli_request *req;
+
+ mid = transport->next_mid;
+
+again:
+ /* now check to see if this mid is being used by one of the
+ pending requests. This is quite efficient because the list is
+ usually very short */
+
+ /* the zero mid is reserved for requests that don't have a mid */
+ if (mid == 0) mid = 1;
+
+ for (req=transport->pending_recv; req; req=req->next) {
+ if (req->mid == mid) {
+ mid++;
+ goto again;
+ }
+ }
+
+ transport->next_mid = mid+1;
+ return mid;
+}
+
+static void idle_handler(struct tevent_context *ev,
+ struct tevent_timer *te, struct timeval t, void *private_data)
+{
+ struct smbcli_transport *transport = talloc_get_type(private_data,
+ struct smbcli_transport);
+ struct timeval next = timeval_add(&t, 0, transport->idle.period);
+ transport->socket->event.te = event_add_timed(transport->socket->event.ctx,
+ transport,
+ next,
+ idle_handler, transport);
+ transport->idle.func(transport, transport->idle.private_data);
+}
+
+/*
+ setup the idle handler for a transport
+ the period is in microseconds
+*/
+_PUBLIC_ void smbcli_transport_idle_handler(struct smbcli_transport *transport,
+ void (*idle_func)(struct smbcli_transport *, void *),
+ uint64_t period,
+ void *private_data)
+{
+ transport->idle.func = idle_func;
+ transport->idle.private_data = private_data;
+ transport->idle.period = period;
+
+ if (transport->socket->event.te != NULL) {
+ talloc_free(transport->socket->event.te);
+ }
+
+ transport->socket->event.te = event_add_timed(transport->socket->event.ctx,
+ transport,
+ timeval_current_ofs(0, period),
+ idle_handler, transport);
+}
+
+/*
+ we have a full request in our receive buffer - match it to a pending request
+ and process
+ */
+static NTSTATUS smbcli_transport_finish_recv(void *private_data, DATA_BLOB blob)
+{
+ struct smbcli_transport *transport = talloc_get_type(private_data,
+ struct smbcli_transport);
+ uint8_t *buffer, *hdr, *vwv;
+ int len;
+ uint16_t wct=0, mid = 0, op = 0;
+ struct smbcli_request *req = NULL;
+
+ buffer = blob.data;
+ len = blob.length;
+
+ hdr = buffer+NBT_HDR_SIZE;
+ vwv = hdr + HDR_VWV;
+
+ /* see if it could be an oplock break request */
+ if (smbcli_handle_oplock_break(transport, len, hdr, vwv)) {
+ talloc_free(buffer);
+ return NT_STATUS_OK;
+ }
+
+ /* at this point we need to check for a readbraw reply, as
+ these can be any length */
+ if (transport->readbraw_pending) {
+ transport->readbraw_pending = 0;
+
+ /* it must match the first entry in the pending queue
+ as the client is not allowed to have outstanding
+ readbraw requests */
+ req = transport->pending_recv;
+ if (!req) goto error;
+
+ req->in.buffer = buffer;
+ talloc_steal(req, buffer);
+ req->in.size = len;
+ req->in.allocated = req->in.size;
+ goto async;
+ }
+
+ if (len >= MIN_SMB_SIZE) {
+ /* extract the mid for matching to pending requests */
+ mid = SVAL(hdr, HDR_MID);
+ wct = CVAL(hdr, HDR_WCT);
+ op = CVAL(hdr, HDR_COM);
+ }
+
+ /* match the incoming request against the list of pending requests */
+ for (req=transport->pending_recv; req; req=req->next) {
+ if (req->mid == mid) break;
+ }
+
+ /* see if it's a ntcancel reply for the current MID */
+ req = smbcli_handle_ntcancel_reply(req, len, hdr);
+
+ if (!req) {
+ DEBUG(1,("Discarding unmatched reply with mid %d op %d\n", mid, op));
+ goto error;
+ }
+
+ /* fill in the 'in' portion of the matching request */
+ req->in.buffer = buffer;
+ talloc_steal(req, buffer);
+ req->in.size = len;
+ req->in.allocated = req->in.size;
+
+ /* handle NBT session replies */
+ if (req->in.size >= 4 && req->in.buffer[0] != 0) {
+ req->status = NT_STATUS_OK;
+ goto async;
+ }
+
+ /* handle non-SMB replies */
+ if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ goto error;
+ }
+
+ if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
+ DEBUG(2,("bad reply size for mid %d\n", mid));
+ req->status = NT_STATUS_UNSUCCESSFUL;
+ req->state = SMBCLI_REQUEST_ERROR;
+ goto error;
+ }
+
+ req->in.hdr = hdr;
+ req->in.vwv = vwv;
+ req->in.wct = wct;
+ if (req->in.size >= NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
+ req->in.data = req->in.vwv + VWV(wct) + 2;
+ req->in.data_size = SVAL(req->in.vwv, VWV(wct));
+ if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + req->in.data_size) {
+ DEBUG(3,("bad data size for mid %d\n", mid));
+ /* blergh - w2k3 gives a bogus data size values in some
+ openX replies */
+ req->in.data_size = req->in.size - (NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct));
+ }
+ }
+ req->in.ptr = req->in.data;
+ req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
+
+ smb_setup_bufinfo(req);
+
+ if (!(req->flags2 & FLAGS2_32_BIT_ERROR_CODES)) {
+ int eclass = CVAL(req->in.hdr,HDR_RCLS);
+ int code = SVAL(req->in.hdr,HDR_ERR);
+ if (eclass == 0 && code == 0) {
+ transport->error.e.nt_status = NT_STATUS_OK;
+ } else {
+ transport->error.e.nt_status = NT_STATUS_DOS(eclass, code);
+ }
+ } else {
+ transport->error.e.nt_status = NT_STATUS(IVAL(req->in.hdr, HDR_RCLS));
+ }
+
+ req->status = transport->error.e.nt_status;
+ if (NT_STATUS_IS_OK(req->status)) {
+ transport->error.etype = ETYPE_NONE;
+ } else {
+ transport->error.etype = ETYPE_SMB;
+ }
+
+ if (!smbcli_request_check_sign_mac(req)) {
+ transport->error.etype = ETYPE_SOCKET;
+ transport->error.e.socket_error = SOCKET_READ_BAD_SIG;
+ req->state = SMBCLI_REQUEST_ERROR;
+ req->status = NT_STATUS_ACCESS_DENIED;
+ goto error;
+ };
+
+async:
+ /* if this request has an async handler then call that to
+ notify that the reply has been received. This might destroy
+ the request so it must happen last */
+
+ req->state = SMBCLI_REQUEST_DONE;
+
+ if (req->recv_helper.fn) {
+ /*
+ * let the recv helper decide in
+ * what state the request really is
+ */
+ req->state = req->recv_helper.fn(req);
+
+ /* if more parts are needed, wait for them */
+ if (req->state <= SMBCLI_REQUEST_RECV) {
+ return NT_STATUS_OK;
+ }
+ }
+ DLIST_REMOVE(transport->pending_recv, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return NT_STATUS_OK;
+
+error:
+ if (req) {
+ DLIST_REMOVE(transport->pending_recv, req);
+ req->state = SMBCLI_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ } else {
+ talloc_free(buffer);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ process some read/write requests that are pending
+ return false if the socket is dead
+*/
+_PUBLIC_ bool smbcli_transport_process(struct smbcli_transport *transport)
+{
+ NTSTATUS status;
+ size_t npending;
+
+ packet_queue_run(transport->packet);
+ if (transport->socket->sock == NULL) {
+ return false;
+ }
+
+ status = socket_pending(transport->socket->sock, &npending);
+ if (NT_STATUS_IS_OK(status) && npending > 0) {
+ packet_recv(transport->packet);
+ }
+ if (transport->socket->sock == NULL) {
+ return false;
+ }
+ return true;
+}
+
+/*
+ handle timeouts of individual smb requests
+*/
+static void smbcli_timeout_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct smbcli_request *req = talloc_get_type(private_data, struct smbcli_request);
+
+ if (req->state == SMBCLI_REQUEST_RECV) {
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ req->status = NT_STATUS_IO_TIMEOUT;
+ req->state = SMBCLI_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+
+/*
+ destroy a request
+*/
+static int smbcli_request_destructor(struct smbcli_request *req)
+{
+ if (req->state == SMBCLI_REQUEST_RECV) {
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ return 0;
+}
+
+
+/*
+ put a request into the send queue
+*/
+void smbcli_transport_send(struct smbcli_request *req)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ /* check if the transport is dead */
+ if (req->transport->socket->sock == NULL) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ req->status = NT_STATUS_NET_WRITE_FAULT;
+ return;
+ }
+
+ blob = data_blob_const(req->out.buffer, req->out.size);
+ status = packet_send(req->transport->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ req->status = status;
+ return;
+ }
+
+ if (req->one_way_request) {
+ req->state = SMBCLI_REQUEST_DONE;
+ smbcli_request_destroy(req);
+ return;
+ }
+
+ req->state = SMBCLI_REQUEST_RECV;
+ DLIST_ADD(req->transport->pending_recv, req);
+
+ /* add a timeout */
+ if (req->transport->options.request_timeout) {
+ event_add_timed(req->transport->socket->event.ctx, req,
+ timeval_current_ofs(req->transport->options.request_timeout, 0),
+ smbcli_timeout_handler, req);
+ }
+
+ talloc_set_destructor(req, smbcli_request_destructor);
+}
+
+
+/****************************************************************************
+ Send an SMBecho (async send)
+*****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_echo_send(struct smbcli_transport *transport,
+ struct smb_echo *p)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup_transport(transport, SMBecho, 1, p->in.size);
+ if (!req) return NULL;
+
+ SSVAL(req->out.vwv, VWV(0), p->in.repeat_count);
+
+ memcpy(req->out.data, p->in.data, p->in.size);
+
+ ZERO_STRUCT(p->out);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ raw echo interface (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_echo_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx,
+ struct smb_echo *p)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ SMBCLI_CHECK_WCT(req, 1);
+ p->out.count++;
+ p->out.sequence_number = SVAL(req->in.vwv, VWV(0));
+ p->out.size = req->in.data_size;
+ talloc_free(p->out.data);
+ p->out.data = talloc_array(mem_ctx, uint8_t, p->out.size);
+ NT_STATUS_HAVE_NO_MEMORY(p->out.data);
+
+ if (!smbcli_raw_pull_data(&req->in.bufinfo, req->in.data, p->out.size, p->out.data)) {
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ if (p->out.count == p->in.repeat_count) {
+ return smbcli_request_destroy(req);
+ }
+
+ return NT_STATUS_OK;
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+/****************************************************************************
+ Send a echo (sync interface)
+*****************************************************************************/
+NTSTATUS smb_raw_echo(struct smbcli_transport *transport, struct smb_echo *p)
+{
+ struct smbcli_request *req = smb_raw_echo_send(transport, p);
+ return smbcli_request_simple_recv(req);
+}
diff --git a/source4/libcli/raw/clitree.c b/source4/libcli/raw/clitree.c
new file mode 100644
index 0000000000..a083396f35
--- /dev/null
+++ b/source4/libcli/raw/clitree.c
@@ -0,0 +1,216 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB client tree context management functions
+
+ Copyright (C) Andrew Tridgell 1994-2005
+ Copyright (C) James Myers 2003 <myersjj@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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+#define SETUP_REQUEST_TREE(cmd, wct, buflen) do { \
+ req = smbcli_request_setup(tree, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+/****************************************************************************
+ Initialize the tree context
+****************************************************************************/
+_PUBLIC_ struct smbcli_tree *smbcli_tree_init(struct smbcli_session *session,
+ TALLOC_CTX *parent_ctx, bool primary)
+{
+ struct smbcli_tree *tree;
+
+ tree = talloc_zero(parent_ctx, struct smbcli_tree);
+ if (!tree) {
+ return NULL;
+ }
+
+ if (primary) {
+ tree->session = talloc_steal(tree, session);
+ } else {
+ tree->session = talloc_reference(tree, session);
+ }
+
+
+ return tree;
+}
+
+/****************************************************************************
+ Send a tconX (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_tcon_send(struct smbcli_tree *tree,
+ union smb_tcon *parms)
+{
+ struct smbcli_request *req = NULL;
+
+ switch (parms->tcon.level) {
+ case RAW_TCON_TCON:
+ SETUP_REQUEST_TREE(SMBtcon, 0, 0);
+ smbcli_req_append_ascii4(req, parms->tcon.in.service, STR_ASCII);
+ smbcli_req_append_ascii4(req, parms->tcon.in.password,STR_ASCII);
+ smbcli_req_append_ascii4(req, parms->tcon.in.dev, STR_ASCII);
+ break;
+
+ case RAW_TCON_TCONX:
+ SETUP_REQUEST_TREE(SMBtconX, 4, 0);
+ SSVAL(req->out.vwv, VWV(0), 0xFF);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->tconx.in.flags);
+ SSVAL(req->out.vwv, VWV(3), parms->tconx.in.password.length);
+ smbcli_req_append_blob(req, &parms->tconx.in.password);
+ smbcli_req_append_string(req, parms->tconx.in.path, STR_TERMINATE | STR_UPPER);
+ smbcli_req_append_string(req, parms->tconx.in.device, STR_TERMINATE | STR_ASCII);
+ break;
+
+ case RAW_TCON_SMB2:
+ return NULL;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Send a tconX (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_tcon_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx,
+ union smb_tcon *parms)
+{
+ uint8_t *p;
+
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ switch (parms->tcon.level) {
+ case RAW_TCON_TCON:
+ SMBCLI_CHECK_WCT(req, 2);
+ parms->tcon.out.max_xmit = SVAL(req->in.vwv, VWV(0));
+ parms->tcon.out.tid = SVAL(req->in.vwv, VWV(1));
+ break;
+
+ case RAW_TCON_TCONX:
+ ZERO_STRUCT(parms->tconx.out);
+ parms->tconx.out.tid = SVAL(req->in.hdr, HDR_TID);
+ if (req->in.wct >= 4) {
+ parms->tconx.out.options = SVAL(req->in.vwv, VWV(3));
+ }
+
+ /* output is actual service name */
+ p = req->in.data;
+ if (!p) break;
+
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->tconx.out.dev_type,
+ p, -1, STR_ASCII | STR_TERMINATE);
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->tconx.out.fs_type,
+ p, -1, STR_TERMINATE);
+ break;
+
+ case RAW_TCON_SMB2:
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+/****************************************************************************
+ Send a tconX (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_tcon(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx,
+ union smb_tcon *parms)
+{
+ struct smbcli_request *req = smb_raw_tcon_send(tree, parms);
+ return smb_raw_tcon_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ Send a tree disconnect.
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_tree_disconnect(struct smbcli_tree *tree)
+{
+ struct smbcli_request *req;
+
+ if (!tree) return NT_STATUS_OK;
+ req = smbcli_request_setup(tree, SMBtdis, 0, 0);
+
+ if (smbcli_request_send(req)) {
+ (void) smbcli_request_receive(req);
+ }
+ return smbcli_request_destroy(req);
+}
+
+
+/*
+ a convenient function to establish a smbcli_tree from scratch
+*/
+NTSTATUS smbcli_tree_full_connection(TALLOC_CTX *parent_ctx,
+ struct smbcli_tree **ret_tree,
+ const char *dest_host, const char **dest_ports,
+ const char *service, const char *service_type,
+ const char *socket_options,
+ struct cli_credentials *credentials,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *ev,
+ struct smbcli_options *options,
+ struct smbcli_session_options *session_options,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct gensec_settings *gensec_settings)
+{
+ struct smb_composite_connect io;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(parent_ctx);
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ io.in.dest_host = dest_host;
+ io.in.dest_ports = dest_ports;
+ io.in.socket_options = socket_options;
+ io.in.called_name = strupper_talloc(tmp_ctx, dest_host);
+ io.in.service = service;
+ io.in.service_type = service_type;
+ io.in.credentials = credentials;
+ io.in.gensec_settings = gensec_settings;
+ io.in.fallback_to_anonymous = false;
+
+ /* This workgroup gets sent out by the SPNEGO session setup.
+ * I don't know of any servers that look at it, so we
+ * hardcode it to "". */
+ io.in.workgroup = "";
+ io.in.options = *options;
+ io.in.session_options = *session_options;
+ io.in.iconv_convenience = iconv_convenience;
+
+ status = smb_composite_connect(&io, parent_ctx, resolve_ctx, ev);
+ if (NT_STATUS_IS_OK(status)) {
+ *ret_tree = io.out.tree;
+ }
+ talloc_free(tmp_ctx);
+ return status;
+}
diff --git a/source4/libcli/raw/interfaces.h b/source4/libcli/raw/interfaces.h
new file mode 100644
index 0000000000..a0584c0aa4
--- /dev/null
+++ b/source4/libcli/raw/interfaces.h
@@ -0,0 +1,2766 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB request interface structures
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) James Peach 2007
+
+ 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_RAW_INTERFACES_H__
+#define __LIBCLI_RAW_INTERFACES_H__
+
+#include "smb.h"
+#include "librpc/gen_ndr/misc.h" /* for struct GUID */
+
+/* this structure is just a wrapper for a string, the only reason we
+ bother with this is that it allows us to check the length provided
+ on the wire in testsuite test code to ensure that we are
+ terminating names in the same way that win2003 is. The *ONLY* time
+ you should ever look at the 'private_length' field in this
+ structure is inside compliance test code, in all other cases just
+ use the null terminated char* as the definitive definition of the
+ string
+
+ also note that this structure is only used in packets where there
+ is an explicit length provided on the wire (hence the name). That
+ length is placed in 'private_length'. For packets where the length
+ is always determined by NULL or packet termination a normal char*
+ is used in the structure definition.
+ */
+struct smb_wire_string {
+ uint32_t private_length;
+ const char *s;
+};
+
+/*
+ * SMB2 uses a 16Byte handle,
+ * (we can maybe use struct GUID later)
+ */
+struct smb2_handle {
+ uint64_t data[2];
+};
+
+struct ntvfs_handle;
+
+/*
+ * a generic container for file handles or file pathes
+ * for qfileinfo/setfileinfo and qpathinfo/setpathinfo
+*/
+union smb_handle_or_path {
+ /*
+ * this is used for
+ * the qpathinfo and setpathinfo
+ * calls
+ */
+ const char *path;
+ /*
+ * this is used as file handle in SMB
+ */
+ uint16_t fnum;
+
+ /*
+ * this is used as file handle in SMB2
+ */
+ struct smb2_handle handle;
+
+ /*
+ * this is used as generic file handle for the NTVFS layer
+ */
+ struct ntvfs_handle *ntvfs;
+};
+
+/*
+ a generic container for file handles
+*/
+union smb_handle {
+ /*
+ * this is used as file handle in SMB
+ */
+ uint16_t fnum;
+
+ /*
+ * this is used as file handle in SMB2
+ */
+ struct smb2_handle handle;
+
+ /*
+ * this is used as generic file handle for the NTVFS layer
+ */
+ struct ntvfs_handle *ntvfs;
+};
+
+/*
+ this header defines the structures and unions used between the SMB
+ parser and the backends.
+*/
+
+/* struct used for SMBlseek call */
+union smb_seek {
+ struct {
+ struct {
+ union smb_handle file;
+ uint16_t mode;
+ int32_t offset; /* signed */
+ } in;
+ struct {
+ int32_t offset;
+ } out;
+ } lseek, generic;
+};
+
+/* struct used in unlink() call */
+union smb_unlink {
+ struct {
+ struct {
+ const char *pattern;
+ uint16_t attrib;
+ } in;
+ } unlink;
+};
+
+
+/* struct used in chkpath() call */
+union smb_chkpath {
+ struct {
+ struct {
+ const char *path;
+ } in;
+ } chkpath;
+};
+
+enum smb_mkdir_level {RAW_MKDIR_GENERIC, RAW_MKDIR_MKDIR, RAW_MKDIR_T2MKDIR};
+
+/* union used in mkdir() call */
+union smb_mkdir {
+ /* generic level */
+ struct {
+ enum smb_mkdir_level level;
+ } generic;
+
+ struct {
+ enum smb_mkdir_level level;
+ struct {
+ const char *path;
+ } in;
+ } mkdir;
+
+ struct {
+ enum smb_mkdir_level level;
+ struct {
+ const char *path;
+ uint_t num_eas;
+ struct ea_struct *eas;
+ } in;
+ } t2mkdir;
+};
+
+/* struct used in rmdir() call */
+struct smb_rmdir {
+ struct {
+ const char *path;
+ } in;
+};
+
+/* struct used in rename() call */
+enum smb_rename_level {RAW_RENAME_RENAME, RAW_RENAME_NTRENAME, RAW_RENAME_NTTRANS};
+
+union smb_rename {
+ struct {
+ enum smb_rename_level level;
+ } generic;
+
+ /* SMBrename interface */
+ struct {
+ enum smb_rename_level level;
+
+ struct {
+ const char *pattern1;
+ const char *pattern2;
+ uint16_t attrib;
+ } in;
+ } rename;
+
+
+ /* SMBntrename interface */
+ struct {
+ enum smb_rename_level level;
+
+ struct {
+ uint16_t attrib;
+ uint16_t flags; /* see RENAME_FLAG_* */
+ uint32_t cluster_size;
+ const char *old_name;
+ const char *new_name;
+ } in;
+ } ntrename;
+
+ /* NT TRANS rename interface */
+ struct {
+ enum smb_rename_level level;
+
+ struct {
+ union smb_handle file;
+ uint16_t flags;/* see RENAME_REPLACE_IF_EXISTS */
+ const char *new_name;
+ } in;
+ } nttrans;
+};
+
+enum smb_tcon_level {
+ RAW_TCON_TCON,
+ RAW_TCON_TCONX,
+ RAW_TCON_SMB2
+};
+
+/* union used in tree connect call */
+union smb_tcon {
+ /* generic interface */
+ struct {
+ enum smb_tcon_level level;
+ } generic;
+
+ /* SMBtcon interface */
+ struct {
+ enum smb_tcon_level level;
+
+ struct {
+ const char *service;
+ const char *password;
+ const char *dev;
+ } in;
+ struct {
+ uint16_t max_xmit;
+ uint16_t tid;
+ } out;
+ } tcon;
+
+ /* SMBtconX interface */
+ struct {
+ enum smb_tcon_level level;
+
+ struct {
+ uint16_t flags;
+ DATA_BLOB password;
+ const char *path;
+ const char *device;
+ } in;
+ struct {
+ uint16_t options;
+ char *dev_type;
+ char *fs_type;
+ uint16_t tid;
+ } out;
+ } tconx;
+
+ /* SMB2 TreeConnect */
+ struct smb2_tree_connect {
+ enum smb_tcon_level level;
+
+ struct {
+ /* static body buffer 8 (0x08) bytes */
+ uint16_t reserved;
+ /* uint16_t path_ofs */
+ /* uint16_t path_size */
+ /* dynamic body */
+ const char *path; /* as non-terminated UTF-16 on the wire */
+ } in;
+ struct {
+ /* static body buffer 16 (0x10) bytes */
+ /* uint16_t buffer_code; 0x10 */
+ uint8_t share_type;
+ uint8_t reserved;
+ uint32_t flags;
+ uint32_t capabilities;
+ uint32_t access_mask;
+
+ /* extracted from the SMB2 header */
+ uint32_t tid;
+ } out;
+ } smb2;
+};
+
+
+enum smb_sesssetup_level {
+ RAW_SESSSETUP_OLD,
+ RAW_SESSSETUP_NT1,
+ RAW_SESSSETUP_SPNEGO,
+ RAW_SESSSETUP_SMB2
+};
+
+/* union used in session_setup call */
+union smb_sesssetup {
+ /* the pre-NT1 interface */
+ struct {
+ enum smb_sesssetup_level level;
+
+ struct {
+ uint16_t bufsize;
+ uint16_t mpx_max;
+ uint16_t vc_num;
+ uint32_t sesskey;
+ DATA_BLOB password;
+ const char *user;
+ const char *domain;
+ const char *os;
+ const char *lanman;
+ } in;
+ struct {
+ uint16_t action;
+ uint16_t vuid;
+ char *os;
+ char *lanman;
+ char *domain;
+ } out;
+ } old;
+
+ /* the NT1 interface */
+ struct {
+ enum smb_sesssetup_level level;
+
+ struct {
+ uint16_t bufsize;
+ uint16_t mpx_max;
+ uint16_t vc_num;
+ uint32_t sesskey;
+ uint32_t capabilities;
+ DATA_BLOB password1;
+ DATA_BLOB password2;
+ const char *user;
+ const char *domain;
+ const char *os;
+ const char *lanman;
+ } in;
+ struct {
+ uint16_t action;
+ uint16_t vuid;
+ char *os;
+ char *lanman;
+ char *domain;
+ } out;
+ } nt1;
+
+
+ /* the SPNEGO interface */
+ struct {
+ enum smb_sesssetup_level level;
+
+ struct {
+ uint16_t bufsize;
+ uint16_t mpx_max;
+ uint16_t vc_num;
+ uint32_t sesskey;
+ uint32_t capabilities;
+ DATA_BLOB secblob;
+ const char *os;
+ const char *lanman;
+ const char *workgroup;
+ } in;
+ struct {
+ uint16_t action;
+ DATA_BLOB secblob;
+ char *os;
+ char *lanman;
+ char *workgroup;
+ uint16_t vuid;
+ } out;
+ } spnego;
+
+ /* SMB2 SessionSetup */
+ struct smb2_session_setup {
+ enum smb_sesssetup_level level;
+
+ struct {
+ /* static body 24 (0x18) bytes */
+ uint8_t vc_number;
+ uint8_t security_mode;
+ uint32_t capabilities;
+ uint32_t channel;
+ /* uint16_t secblob_ofs */
+ /* uint16_t secblob_size */
+ uint64_t previous_sessionid;
+ /* dynamic body */
+ DATA_BLOB secblob;
+ } in;
+ struct {
+ /* body buffer 8 (0x08) bytes */
+ uint16_t session_flags;
+ /* uint16_t secblob_ofs */
+ /* uint16_t secblob_size */
+ /* dynamic body */
+ DATA_BLOB secblob;
+
+ /* extracted from the SMB2 header */
+ uint64_t uid;
+ } out;
+ } smb2;
+};
+
+/* Note that the specified enum values are identical to the actual info-levels used
+ * on the wire.
+ */
+enum smb_fileinfo_level {
+ RAW_FILEINFO_GENERIC = 0xF000,
+ RAW_FILEINFO_GETATTR, /* SMBgetatr */
+ RAW_FILEINFO_GETATTRE, /* SMBgetattrE */
+ RAW_FILEINFO_SEC_DESC, /* NT_TRANSACT_QUERY_SECURITY_DESC */
+ RAW_FILEINFO_STANDARD = SMB_QFILEINFO_STANDARD,
+ RAW_FILEINFO_EA_SIZE = SMB_QFILEINFO_EA_SIZE,
+ RAW_FILEINFO_EA_LIST = SMB_QFILEINFO_EA_LIST,
+ RAW_FILEINFO_ALL_EAS = SMB_QFILEINFO_ALL_EAS,
+ RAW_FILEINFO_IS_NAME_VALID = SMB_QFILEINFO_IS_NAME_VALID,
+ RAW_FILEINFO_BASIC_INFO = SMB_QFILEINFO_BASIC_INFO,
+ RAW_FILEINFO_STANDARD_INFO = SMB_QFILEINFO_STANDARD_INFO,
+ RAW_FILEINFO_EA_INFO = SMB_QFILEINFO_EA_INFO,
+ RAW_FILEINFO_NAME_INFO = SMB_QFILEINFO_NAME_INFO,
+ RAW_FILEINFO_ALL_INFO = SMB_QFILEINFO_ALL_INFO,
+ RAW_FILEINFO_ALT_NAME_INFO = SMB_QFILEINFO_ALT_NAME_INFO,
+ RAW_FILEINFO_STREAM_INFO = SMB_QFILEINFO_STREAM_INFO,
+ RAW_FILEINFO_COMPRESSION_INFO = SMB_QFILEINFO_COMPRESSION_INFO,
+ RAW_FILEINFO_UNIX_BASIC = SMB_QFILEINFO_UNIX_BASIC,
+ RAW_FILEINFO_UNIX_INFO2 = SMB_QFILEINFO_UNIX_INFO2,
+ RAW_FILEINFO_UNIX_LINK = SMB_QFILEINFO_UNIX_LINK,
+ RAW_FILEINFO_BASIC_INFORMATION = SMB_QFILEINFO_BASIC_INFORMATION,
+ RAW_FILEINFO_STANDARD_INFORMATION = SMB_QFILEINFO_STANDARD_INFORMATION,
+ RAW_FILEINFO_INTERNAL_INFORMATION = SMB_QFILEINFO_INTERNAL_INFORMATION,
+ RAW_FILEINFO_EA_INFORMATION = SMB_QFILEINFO_EA_INFORMATION,
+ RAW_FILEINFO_ACCESS_INFORMATION = SMB_QFILEINFO_ACCESS_INFORMATION,
+ RAW_FILEINFO_NAME_INFORMATION = SMB_QFILEINFO_NAME_INFORMATION,
+ RAW_FILEINFO_POSITION_INFORMATION = SMB_QFILEINFO_POSITION_INFORMATION,
+ RAW_FILEINFO_MODE_INFORMATION = SMB_QFILEINFO_MODE_INFORMATION,
+ RAW_FILEINFO_ALIGNMENT_INFORMATION = SMB_QFILEINFO_ALIGNMENT_INFORMATION,
+ RAW_FILEINFO_ALL_INFORMATION = SMB_QFILEINFO_ALL_INFORMATION,
+ RAW_FILEINFO_ALT_NAME_INFORMATION = SMB_QFILEINFO_ALT_NAME_INFORMATION,
+ RAW_FILEINFO_STREAM_INFORMATION = SMB_QFILEINFO_STREAM_INFORMATION,
+ RAW_FILEINFO_COMPRESSION_INFORMATION = SMB_QFILEINFO_COMPRESSION_INFORMATION,
+ RAW_FILEINFO_NETWORK_OPEN_INFORMATION = SMB_QFILEINFO_NETWORK_OPEN_INFORMATION,
+ RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION = SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION,
+ /* SMB2 specific levels */
+ RAW_FILEINFO_SMB2_ALL_EAS = 0x0f01,
+ RAW_FILEINFO_SMB2_ALL_INFORMATION = 0x1201
+};
+
+/* union used in qfileinfo() and qpathinfo() backend calls */
+union smb_fileinfo {
+ /* generic interface:
+ * matches RAW_FILEINFO_GENERIC */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint32_t attrib;
+ uint32_t ea_size;
+ uint_t num_eas;
+ struct ea_struct {
+ uint8_t flags;
+ struct smb_wire_string name;
+ DATA_BLOB value;
+ } *eas;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t nlink;
+ struct smb_wire_string fname;
+ struct smb_wire_string alt_fname;
+ uint8_t delete_pending;
+ uint8_t directory;
+ uint64_t compressed_size;
+ uint16_t format;
+ uint8_t unit_shift;
+ uint8_t chunk_shift;
+ uint8_t cluster_shift;
+ uint64_t file_id;
+ uint32_t access_flags; /* seen 0x001f01ff from w2k3 */
+ uint64_t position;
+ uint32_t mode;
+ uint32_t alignment_requirement;
+ uint32_t reparse_tag;
+ uint_t num_streams;
+ struct stream_struct {
+ uint64_t size;
+ uint64_t alloc_size;
+ struct smb_wire_string stream_name;
+ } *streams;
+ } out;
+ } generic;
+
+
+ /* SMBgetatr interface:
+ * matches RAW_FILEINFO_GETATTR */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint16_t attrib;
+ uint32_t size;
+ time_t write_time;
+ } out;
+ } getattr;
+
+ /* SMBgetattrE and RAW_FILEINFO_STANDARD interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ time_t create_time;
+ time_t access_time;
+ time_t write_time;
+ uint32_t size;
+ uint32_t alloc_size;
+ uint16_t attrib;
+ } out;
+ } getattre, standard;
+
+ /* trans2 RAW_FILEINFO_EA_SIZE interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ time_t create_time;
+ time_t access_time;
+ time_t write_time;
+ uint32_t size;
+ uint32_t alloc_size;
+ uint16_t attrib;
+ uint32_t ea_size;
+ } out;
+ } ea_size;
+
+ /* trans2 RAW_FILEINFO_EA_LIST interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint_t num_names;
+ struct ea_name {
+ struct smb_wire_string name;
+ } *ea_names;
+ } in;
+
+ struct smb_ea_list {
+ uint_t num_eas;
+ struct ea_struct *eas;
+ } out;
+ } ea_list;
+
+ /* trans2 RAW_FILEINFO_ALL_EAS and RAW_FILEINFO_FULL_EA_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ /* SMB2 only - SMB2_CONTINUE_FLAG_* */
+ uint8_t continue_flags;
+ } in;
+ struct smb_ea_list out;
+ } all_eas;
+
+ /* trans2 qpathinfo RAW_FILEINFO_IS_NAME_VALID interface
+ only valid for a QPATHNAME call - no returned data */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ } is_name_valid;
+
+ /* RAW_FILEINFO_BASIC_INFO and RAW_FILEINFO_BASIC_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint32_t attrib;
+ } out;
+ } basic_info;
+
+
+ /* RAW_FILEINFO_STANDARD_INFO and RAW_FILEINFO_STANDARD_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t nlink;
+ bool delete_pending;
+ bool directory;
+ } out;
+ } standard_info;
+
+ /* RAW_FILEINFO_EA_INFO and RAW_FILEINFO_EA_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint32_t ea_size;
+ } out;
+ } ea_info;
+
+ /* RAW_FILEINFO_NAME_INFO and RAW_FILEINFO_NAME_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ struct smb_wire_string fname;
+ } out;
+ } name_info;
+
+ /* RAW_FILEINFO_ALL_INFO and RAW_FILEINFO_ALL_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint32_t attrib;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t nlink;
+ uint8_t delete_pending;
+ uint8_t directory;
+ uint32_t ea_size;
+ struct smb_wire_string fname;
+ } out;
+ } all_info;
+
+ /* RAW_FILEINFO_SMB2_ALL_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint32_t attrib;
+ uint32_t unknown1;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t nlink;
+ uint8_t delete_pending;
+ uint8_t directory;
+ /* uint16_t _pad; */
+ uint64_t file_id;
+ uint32_t ea_size;
+ uint32_t access_mask;
+ uint64_t position;
+ uint32_t mode;
+ uint32_t alignment_requirement;
+ struct smb_wire_string fname;
+ } out;
+ } all_info2;
+
+ /* RAW_FILEINFO_ALT_NAME_INFO and RAW_FILEINFO_ALT_NAME_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ struct smb_wire_string fname;
+ } out;
+ } alt_name_info;
+
+ /* RAW_FILEINFO_STREAM_INFO and RAW_FILEINFO_STREAM_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct stream_information {
+ uint_t num_streams;
+ struct stream_struct *streams;
+ } out;
+ } stream_info;
+
+ /* RAW_FILEINFO_COMPRESSION_INFO and RAW_FILEINFO_COMPRESSION_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint64_t compressed_size;
+ uint16_t format;
+ uint8_t unit_shift;
+ uint8_t chunk_shift;
+ uint8_t cluster_shift;
+ } out;
+ } compression_info;
+
+ /* RAW_FILEINFO_UNIX_BASIC interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint64_t end_of_file;
+ uint64_t num_bytes;
+ NTTIME status_change_time;
+ NTTIME access_time;
+ NTTIME change_time;
+ uint64_t uid;
+ uint64_t gid;
+ uint32_t file_type;
+ uint64_t dev_major;
+ uint64_t dev_minor;
+ uint64_t unique_id;
+ uint64_t permissions;
+ uint64_t nlink;
+ } out;
+ } unix_basic_info;
+
+ /* RAW_FILEINFO_UNIX_INFO2 interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint64_t end_of_file;
+ uint64_t num_bytes;
+ NTTIME status_change_time;
+ NTTIME access_time;
+ NTTIME change_time;
+ uint64_t uid;
+ uint64_t gid;
+ uint32_t file_type;
+ uint64_t dev_major;
+ uint64_t dev_minor;
+ uint64_t unique_id;
+ uint64_t permissions;
+ uint64_t nlink;
+ NTTIME create_time;
+ uint32_t file_flags;
+ uint32_t flags_mask;
+ } out;
+ } unix_info2;
+
+ /* RAW_FILEINFO_UNIX_LINK interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ struct smb_wire_string link_dest;
+ } out;
+ } unix_link_info;
+
+ /* RAW_FILEINFO_INTERNAL_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint64_t file_id;
+ } out;
+ } internal_information;
+
+ /* RAW_FILEINFO_ACCESS_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint32_t access_flags;
+ } out;
+ } access_information;
+
+ /* RAW_FILEINFO_POSITION_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint64_t position;
+ } out;
+ } position_information;
+
+ /* RAW_FILEINFO_MODE_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint32_t mode;
+ } out;
+ } mode_information;
+
+ /* RAW_FILEINFO_ALIGNMENT_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint32_t alignment_requirement;
+ } out;
+ } alignment_information;
+
+ /* RAW_FILEINFO_NETWORK_OPEN_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t attrib;
+ } out;
+ } network_open_information;
+
+
+ /* RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint32_t attrib;
+ uint32_t reparse_tag;
+ } out;
+ } attribute_tag_information;
+
+ /* RAW_FILEINFO_SEC_DESC */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint32_t secinfo_flags;
+ } in;
+ struct {
+ struct security_descriptor *sd;
+ } out;
+ } query_secdesc;
+};
+
+
+enum smb_setfileinfo_level {
+ RAW_SFILEINFO_GENERIC = 0xF000,
+ RAW_SFILEINFO_SETATTR, /* SMBsetatr */
+ RAW_SFILEINFO_SETATTRE, /* SMBsetattrE */
+ RAW_SFILEINFO_SEC_DESC, /* NT_TRANSACT_SET_SECURITY_DESC */
+ RAW_SFILEINFO_STANDARD = SMB_SFILEINFO_STANDARD,
+ RAW_SFILEINFO_EA_SET = SMB_SFILEINFO_EA_SET,
+ RAW_SFILEINFO_BASIC_INFO = SMB_SFILEINFO_BASIC_INFO,
+ RAW_SFILEINFO_DISPOSITION_INFO = SMB_SFILEINFO_DISPOSITION_INFO,
+ RAW_SFILEINFO_ALLOCATION_INFO = SMB_SFILEINFO_ALLOCATION_INFO,
+ RAW_SFILEINFO_END_OF_FILE_INFO = SMB_SFILEINFO_END_OF_FILE_INFO,
+ RAW_SFILEINFO_UNIX_BASIC = SMB_SFILEINFO_UNIX_BASIC,
+ RAW_SFILEINFO_UNIX_INFO2 = SMB_SFILEINFO_UNIX_INFO2,
+ RAW_SFILEINFO_UNIX_LINK = SMB_SFILEINFO_UNIX_LINK,
+ RAW_SFILEINFO_UNIX_HLINK = SMB_SFILEINFO_UNIX_HLINK,
+ RAW_SFILEINFO_BASIC_INFORMATION = SMB_SFILEINFO_BASIC_INFORMATION,
+ RAW_SFILEINFO_RENAME_INFORMATION = SMB_SFILEINFO_RENAME_INFORMATION,
+ RAW_SFILEINFO_LINK_INFORMATION = SMB_SFILEINFO_LINK_INFORMATION,
+ RAW_SFILEINFO_DISPOSITION_INFORMATION = SMB_SFILEINFO_DISPOSITION_INFORMATION,
+ RAW_SFILEINFO_POSITION_INFORMATION = SMB_SFILEINFO_POSITION_INFORMATION,
+ RAW_SFILEINFO_FULL_EA_INFORMATION = SMB_SFILEINFO_FULL_EA_INFORMATION,
+ RAW_SFILEINFO_MODE_INFORMATION = SMB_SFILEINFO_MODE_INFORMATION,
+ RAW_SFILEINFO_ALLOCATION_INFORMATION = SMB_SFILEINFO_ALLOCATION_INFORMATION,
+ RAW_SFILEINFO_END_OF_FILE_INFORMATION = SMB_SFILEINFO_END_OF_FILE_INFORMATION,
+ RAW_SFILEINFO_PIPE_INFORMATION = SMB_SFILEINFO_PIPE_INFORMATION,
+ RAW_SFILEINFO_VALID_DATA_INFORMATION = SMB_SFILEINFO_VALID_DATA_INFORMATION,
+ RAW_SFILEINFO_SHORT_NAME_INFORMATION = SMB_SFILEINFO_SHORT_NAME_INFORMATION,
+ RAW_SFILEINFO_1025 = SMB_SFILEINFO_1025,
+ RAW_SFILEINFO_1027 = SMB_SFILEINFO_1027,
+ RAW_SFILEINFO_1029 = SMB_SFILEINFO_1029,
+ RAW_SFILEINFO_1030 = SMB_SFILEINFO_1030,
+ RAW_SFILEINFO_1031 = SMB_SFILEINFO_1031,
+ RAW_SFILEINFO_1032 = SMB_SFILEINFO_1032,
+ RAW_SFILEINFO_1036 = SMB_SFILEINFO_1036,
+ RAW_SFILEINFO_1041 = SMB_SFILEINFO_1041,
+ RAW_SFILEINFO_1042 = SMB_SFILEINFO_1042,
+ RAW_SFILEINFO_1043 = SMB_SFILEINFO_1043,
+ RAW_SFILEINFO_1044 = SMB_SFILEINFO_1044,
+
+ /* cope with breakage in SMB2 */
+ RAW_SFILEINFO_RENAME_INFORMATION_SMB2 = SMB_SFILEINFO_RENAME_INFORMATION|0x80000000,
+};
+
+/* union used in setfileinfo() and setpathinfo() calls */
+union smb_setfileinfo {
+ /* generic interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ } generic;
+
+ /* RAW_SFILEINFO_SETATTR (SMBsetatr) interface - only via setpathinfo() */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint16_t attrib;
+ time_t write_time;
+ } in;
+ } setattr;
+
+ /* RAW_SFILEINFO_SETATTRE (SMBsetattrE) interface - only via setfileinfo()
+ also RAW_SFILEINFO_STANDARD */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ time_t create_time;
+ time_t access_time;
+ time_t write_time;
+ /* notice that size, alloc_size and attrib are not settable,
+ unlike the corresponding qfileinfo level */
+ } in;
+ } setattre, standard;
+
+ /* RAW_SFILEINFO_EA_SET interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint_t num_eas;
+ struct ea_struct *eas;
+ } in;
+ } ea_set;
+
+ /* RAW_SFILEINFO_BASIC_INFO and
+ RAW_SFILEINFO_BASIC_INFORMATION interfaces */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint32_t attrib;
+ uint32_t reserved;
+ } in;
+ } basic_info;
+
+ /* RAW_SFILEINFO_DISPOSITION_INFO and
+ RAW_SFILEINFO_DISPOSITION_INFORMATION interfaces */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ bool delete_on_close;
+ } in;
+ } disposition_info;
+
+ /* RAW_SFILEINFO_ALLOCATION_INFO and
+ RAW_SFILEINFO_ALLOCATION_INFORMATION interfaces */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ /* w2k3 rounds this up to nearest 4096 */
+ uint64_t alloc_size;
+ } in;
+ } allocation_info;
+
+ /* RAW_SFILEINFO_END_OF_FILE_INFO and
+ RAW_SFILEINFO_END_OF_FILE_INFORMATION interfaces */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint64_t size;
+ } in;
+ } end_of_file_info;
+
+ /* RAW_SFILEINFO_RENAME_INFORMATION interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint8_t overwrite;
+ uint64_t root_fid;
+ const char *new_name;
+ } in;
+ } rename_information;
+
+ /* RAW_SFILEINFO_LINK_INFORMATION interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint8_t overwrite;
+ uint64_t root_fid;
+ const char *new_name;
+ } in;
+ } link_information;
+
+ /* RAW_SFILEINFO_POSITION_INFORMATION interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint64_t position;
+ } in;
+ } position_information;
+
+ /* RAW_SFILEINFO_MODE_INFORMATION interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ /* valid values seem to be 0, 2, 4 and 6 */
+ uint32_t mode;
+ } in;
+ } mode_information;
+
+ /* RAW_SFILEINFO_UNIX_BASIC interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint32_t mode; /* yuck - this field remains to fix compile of libcli/clifile.c */
+ uint64_t end_of_file;
+ uint64_t num_bytes;
+ NTTIME status_change_time;
+ NTTIME access_time;
+ NTTIME change_time;
+ uint64_t uid;
+ uint64_t gid;
+ uint32_t file_type;
+ uint64_t dev_major;
+ uint64_t dev_minor;
+ uint64_t unique_id;
+ uint64_t permissions;
+ uint64_t nlink;
+ } in;
+ } unix_basic;
+
+ /* RAW_SFILEINFO_UNIX_INFO2 interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint64_t end_of_file;
+ uint64_t num_bytes;
+ NTTIME status_change_time;
+ NTTIME access_time;
+ NTTIME change_time;
+ uint64_t uid;
+ uint64_t gid;
+ uint32_t file_type;
+ uint64_t dev_major;
+ uint64_t dev_minor;
+ uint64_t unique_id;
+ uint64_t permissions;
+ uint64_t nlink;
+ NTTIME create_time;
+ uint32_t file_flags;
+ uint32_t flags_mask;
+ } in;
+ } unix_info2;
+
+ /* RAW_SFILEINFO_UNIX_LINK, RAW_SFILEINFO_UNIX_HLINK interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ const char *link_dest;
+ } in;
+ } unix_link, unix_hlink;
+
+ /* RAW_FILEINFO_SET_SEC_DESC */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint32_t secinfo_flags;
+ struct security_descriptor *sd;
+ } in;
+ } set_secdesc;
+
+ /* RAW_SFILEINFO_FULL_EA_INFORMATION */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ struct smb_ea_list eas;
+ } in;
+ } full_ea_information;
+};
+
+
+enum smb_fsinfo_level {
+ RAW_QFS_GENERIC = 0xF000,
+ RAW_QFS_DSKATTR, /* SMBdskattr */
+ RAW_QFS_ALLOCATION = SMB_QFS_ALLOCATION,
+ RAW_QFS_VOLUME = SMB_QFS_VOLUME,
+ RAW_QFS_VOLUME_INFO = SMB_QFS_VOLUME_INFO,
+ RAW_QFS_SIZE_INFO = SMB_QFS_SIZE_INFO,
+ RAW_QFS_DEVICE_INFO = SMB_QFS_DEVICE_INFO,
+ RAW_QFS_ATTRIBUTE_INFO = SMB_QFS_ATTRIBUTE_INFO,
+ RAW_QFS_UNIX_INFO = SMB_QFS_UNIX_INFO,
+ RAW_QFS_VOLUME_INFORMATION = SMB_QFS_VOLUME_INFORMATION,
+ RAW_QFS_SIZE_INFORMATION = SMB_QFS_SIZE_INFORMATION,
+ RAW_QFS_DEVICE_INFORMATION = SMB_QFS_DEVICE_INFORMATION,
+ RAW_QFS_ATTRIBUTE_INFORMATION = SMB_QFS_ATTRIBUTE_INFORMATION,
+ RAW_QFS_QUOTA_INFORMATION = SMB_QFS_QUOTA_INFORMATION,
+ RAW_QFS_FULL_SIZE_INFORMATION = SMB_QFS_FULL_SIZE_INFORMATION,
+ RAW_QFS_OBJECTID_INFORMATION = SMB_QFS_OBJECTID_INFORMATION};
+
+
+/* union for fsinfo() backend call. Note that there are no in
+ structures, as this call only contains out parameters */
+union smb_fsinfo {
+ /* generic interface */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint32_t block_size;
+ uint64_t blocks_total;
+ uint64_t blocks_free;
+ uint32_t fs_id;
+ NTTIME create_time;
+ uint32_t serial_number;
+ uint32_t fs_attr;
+ uint32_t max_file_component_length;
+ uint32_t device_type;
+ uint32_t device_characteristics;
+ uint64_t quota_soft;
+ uint64_t quota_hard;
+ uint64_t quota_flags;
+ struct GUID guid;
+ char *volume_name;
+ char *fs_type;
+ } out;
+ } generic;
+
+ /* SMBdskattr interface */
+ struct {
+ enum smb_fsinfo_level level;
+
+ struct {
+ uint16_t units_total;
+ uint16_t blocks_per_unit;
+ uint16_t block_size;
+ uint16_t units_free;
+ } out;
+ } dskattr;
+
+ /* trans2 RAW_QFS_ALLOCATION interface */
+ struct {
+ enum smb_fsinfo_level level;
+
+ struct {
+ uint32_t fs_id;
+ uint32_t sectors_per_unit;
+ uint32_t total_alloc_units;
+ uint32_t avail_alloc_units;
+ uint16_t bytes_per_sector;
+ } out;
+ } allocation;
+
+ /* TRANS2 RAW_QFS_VOLUME interface */
+ struct {
+ enum smb_fsinfo_level level;
+
+ struct {
+ uint32_t serial_number;
+ struct smb_wire_string volume_name;
+ } out;
+ } volume;
+
+ /* TRANS2 RAW_QFS_VOLUME_INFO and RAW_QFS_VOLUME_INFORMATION interfaces */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ NTTIME create_time;
+ uint32_t serial_number;
+ struct smb_wire_string volume_name;
+ } out;
+ } volume_info;
+
+ /* trans2 RAW_QFS_SIZE_INFO and RAW_QFS_SIZE_INFORMATION interfaces */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint64_t total_alloc_units;
+ uint64_t avail_alloc_units; /* maps to call_avail_alloc_units */
+ uint32_t sectors_per_unit;
+ uint32_t bytes_per_sector;
+ } out;
+ } size_info;
+
+ /* TRANS2 RAW_QFS_DEVICE_INFO and RAW_QFS_DEVICE_INFORMATION interfaces */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint32_t device_type;
+ uint32_t characteristics;
+ } out;
+ } device_info;
+
+
+ /* TRANS2 RAW_QFS_ATTRIBUTE_INFO and RAW_QFS_ATTRIBUTE_INFORMATION interfaces */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint32_t fs_attr;
+ uint32_t max_file_component_length;
+ struct smb_wire_string fs_type;
+ } out;
+ } attribute_info;
+
+
+ /* TRANS2 RAW_QFS_UNIX_INFO interface */
+ struct {
+ enum smb_fsinfo_level level;
+
+ struct {
+ uint16_t major_version;
+ uint16_t minor_version;
+ uint64_t capability;
+ } out;
+ } unix_info;
+
+ /* trans2 RAW_QFS_QUOTA_INFORMATION interface */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint64_t unknown[3];
+ uint64_t quota_soft;
+ uint64_t quota_hard;
+ uint64_t quota_flags;
+ } out;
+ } quota_information;
+
+ /* trans2 RAW_QFS_FULL_SIZE_INFORMATION interface */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint64_t total_alloc_units;
+ uint64_t call_avail_alloc_units;
+ uint64_t actual_avail_alloc_units;
+ uint32_t sectors_per_unit;
+ uint32_t bytes_per_sector;
+ } out;
+ } full_size_information;
+
+ /* trans2 RAW_QFS_OBJECTID_INFORMATION interface */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ struct GUID guid;
+ uint64_t unknown[6];
+ } out;
+ } objectid_information;
+};
+
+
+
+enum smb_open_level {
+ RAW_OPEN_OPEN,
+ RAW_OPEN_OPENX,
+ RAW_OPEN_MKNEW,
+ RAW_OPEN_CREATE,
+ RAW_OPEN_CTEMP,
+ RAW_OPEN_SPLOPEN,
+ RAW_OPEN_NTCREATEX,
+ RAW_OPEN_T2OPEN,
+ RAW_OPEN_NTTRANS_CREATE,
+ RAW_OPEN_OPENX_READX,
+ RAW_OPEN_SMB2
+};
+
+/* the generic interface is defined to be equal to the NTCREATEX interface */
+#define RAW_OPEN_GENERIC RAW_OPEN_NTCREATEX
+
+/* union for open() backend call */
+union smb_open {
+/*
+ * because the *.out.file structs are not aligned to the same offset for each level
+ * we provide a hepler macro that should be used to find the current smb_handle structure
+ */
+#define SMB_OPEN_OUT_FILE(op, file) do { \
+ switch (op->generic.level) { \
+ case RAW_OPEN_OPEN: \
+ file = &op->openold.out.file; \
+ break; \
+ case RAW_OPEN_OPENX: \
+ file = &op->openx.out.file; \
+ break; \
+ case RAW_OPEN_MKNEW: \
+ file = &op->mknew.out.file; \
+ break; \
+ case RAW_OPEN_CREATE: \
+ file = &op->create.out.file; \
+ break; \
+ case RAW_OPEN_CTEMP: \
+ file = &op->ctemp.out.file; \
+ break; \
+ case RAW_OPEN_SPLOPEN: \
+ file = &op->splopen.out.file; \
+ break; \
+ case RAW_OPEN_NTCREATEX: \
+ file = &op->ntcreatex.out.file; \
+ break; \
+ case RAW_OPEN_T2OPEN: \
+ file = &op->t2open.out.file; \
+ break; \
+ case RAW_OPEN_NTTRANS_CREATE: \
+ file = &op->nttrans.out.file; \
+ break; \
+ case RAW_OPEN_OPENX_READX: \
+ file = &op->openxreadx.out.file; \
+ break; \
+ case RAW_OPEN_SMB2: \
+ file = &op->smb2.out.file; \
+ break; \
+ default: \
+ /* this must be a programmer error */ \
+ file = NULL; \
+ break; \
+ } \
+} while (0)
+ /* SMBNTCreateX, nttrans and generic interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint32_t flags;
+ uint32_t root_fid;
+ uint32_t access_mask;
+ uint64_t alloc_size;
+ uint32_t file_attr;
+ uint32_t share_access;
+ uint32_t open_disposition;
+ uint32_t create_options;
+ uint32_t impersonation;
+ uint8_t security_flags;
+ /* NOTE: fname can also be a pointer to a
+ uint64_t file_id if create_options has the
+ NTCREATEX_OPTIONS_OPEN_BY_FILE_ID flag set */
+ const char *fname;
+
+ /* these last 2 elements are only used in the
+ NTTRANS varient of the call */
+ struct security_descriptor *sec_desc;
+ struct smb_ea_list *ea_list;
+
+ /* some optional parameters from the SMB2 varient */
+ bool query_maximal_access;
+ } in;
+ struct {
+ union smb_handle file;
+ uint8_t oplock_level;
+ uint32_t create_action;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint32_t attrib;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint16_t file_type;
+ uint16_t ipc_state;
+ uint8_t is_directory;
+
+ /* optional return values matching SMB2 tagged
+ values in the call */
+ uint32_t maximal_access;
+ } out;
+ } ntcreatex, nttrans, generic;
+
+ /* TRANS2_OPEN interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t flags;
+ uint16_t open_mode;
+ uint16_t search_attrs;
+ uint16_t file_attrs;
+ time_t write_time;
+ uint16_t open_func;
+ uint32_t size;
+ uint32_t timeout;
+ const char *fname;
+ uint_t num_eas;
+ struct ea_struct *eas;
+ } in;
+ struct {
+ union smb_handle file;
+ uint16_t attrib;
+ time_t write_time;
+ uint32_t size;
+ uint16_t access;
+ uint16_t ftype;
+ uint16_t devstate;
+ uint16_t action;
+ uint32_t file_id;
+ } out;
+ } t2open;
+
+ /* SMBopen interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t open_mode;
+ uint16_t search_attrs;
+ const char *fname;
+ } in;
+ struct {
+ union smb_handle file;
+ uint16_t attrib;
+ time_t write_time;
+ uint32_t size;
+ uint16_t rmode;
+ } out;
+ } openold;
+
+ /* SMBopenX interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t flags;
+ uint16_t open_mode;
+ uint16_t search_attrs; /* not honoured by win2003 */
+ uint16_t file_attrs;
+ time_t write_time; /* not honoured by win2003 */
+ uint16_t open_func;
+ uint32_t size; /* note that this sets the
+ initial file size, not
+ just allocation size */
+ uint32_t timeout; /* not honoured by win2003 */
+ const char *fname;
+ } in;
+ struct {
+ union smb_handle file;
+ uint16_t attrib;
+ time_t write_time;
+ uint32_t size;
+ uint16_t access;
+ uint16_t ftype;
+ uint16_t devstate;
+ uint16_t action;
+ uint32_t unique_fid;
+ uint32_t access_mask;
+ uint32_t unknown;
+ } out;
+ } openx;
+
+ /* SMBmknew interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t attrib;
+ time_t write_time;
+ const char *fname;
+ } in;
+ struct {
+ union smb_handle file;
+ } out;
+ } mknew, create;
+
+ /* SMBctemp interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t attrib;
+ time_t write_time;
+ const char *directory;
+ } in;
+ struct {
+ union smb_handle file;
+ /* temp name, relative to directory */
+ char *name;
+ } out;
+ } ctemp;
+
+ /* SMBsplopen interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t setup_length;
+ uint16_t mode;
+ const char *ident;
+ } in;
+ struct {
+ union smb_handle file;
+ } out;
+ } splopen;
+
+
+ /* chained OpenX/ReadX interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t flags;
+ uint16_t open_mode;
+ uint16_t search_attrs; /* not honoured by win2003 */
+ uint16_t file_attrs;
+ time_t write_time; /* not honoured by win2003 */
+ uint16_t open_func;
+ uint32_t size; /* note that this sets the
+ initial file size, not
+ just allocation size */
+ uint32_t timeout; /* not honoured by win2003 */
+ const char *fname;
+
+ /* readx part */
+ uint64_t offset;
+ uint16_t mincnt;
+ uint32_t maxcnt;
+ uint16_t remaining;
+ } in;
+ struct {
+ union smb_handle file;
+ uint16_t attrib;
+ time_t write_time;
+ uint32_t size;
+ uint16_t access;
+ uint16_t ftype;
+ uint16_t devstate;
+ uint16_t action;
+ uint32_t unique_fid;
+ uint32_t access_mask;
+ uint32_t unknown;
+
+ /* readx part */
+ uint8_t *data;
+ uint16_t remaining;
+ uint16_t compaction_mode;
+ uint16_t nread;
+ } out;
+ } openxreadx;
+
+#define SMB2_CREATE_FLAG_REQUEST_OPLOCK 0x0100
+#define SMB2_CREATE_FLAG_REQUEST_EXCLUSIVE_OPLOCK 0x0800
+#define SMB2_CREATE_FLAG_GRANT_OPLOCK 0x0001
+#define SMB2_CREATE_FLAG_GRANT_EXCLUSIVE_OPLOCK 0x0080
+
+ /* SMB2 Create */
+ struct smb2_create {
+ enum smb_open_level level;
+ struct {
+ /* static body buffer 56 (0x38) bytes */
+ uint8_t security_flags; /* SMB2_SECURITY_* */
+ uint8_t oplock_level; /* SMB2_OPLOCK_LEVEL_* */
+ uint32_t impersonation_level; /* SMB2_IMPERSONATION_* */
+ uint64_t create_flags;
+ uint64_t reserved;
+ uint32_t desired_access;
+ uint32_t file_attributes;
+ uint32_t share_access; /* NTCREATEX_SHARE_ACCESS_* */
+ uint32_t create_disposition; /* NTCREATEX_DISP_* */
+ uint32_t create_options; /* NTCREATEX_OPTIONS_* */
+
+ /* uint16_t fname_ofs */
+ /* uint16_t fname_size */
+ /* uint32_t blob_ofs; */
+ /* uint32_t blob_size; */
+
+ /* dynamic body */
+ const char *fname;
+
+ /* now some optional parameters - encoded as tagged blobs */
+ struct smb_ea_list eas;
+ uint64_t alloc_size;
+ struct security_descriptor *sec_desc;
+ bool durable_open;
+ struct smb2_handle *durable_handle;
+ bool query_maximal_access;
+ NTTIME timewarp;
+ bool query_on_disk_id;
+
+ /* and any additional blobs the caller wants */
+ struct smb2_create_blobs {
+ uint32_t num_blobs;
+ struct smb2_create_blob {
+ const char *tag;
+ DATA_BLOB data;
+ } *blobs;
+ } blobs;
+ } in;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 88 (0x58) bytes */
+ /* uint16_t buffer_code; 0x59 = 0x58 + 1 */
+ uint8_t oplock_level;
+ uint8_t reserved;
+ uint32_t create_action;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t file_attr;
+ uint32_t reserved2;
+ /* struct smb2_handle handle;*/
+ /* uint32_t blob_ofs; */
+ /* uint32_t blob_size; */
+
+ /* optional return values matching tagged values in the call */
+ uint32_t maximal_access;
+ uint8_t on_disk_id[32];
+
+ /* tagged blobs in the reply */
+ struct smb2_create_blobs blobs;
+ } out;
+ } smb2;
+};
+
+
+
+enum smb_read_level {
+ RAW_READ_READBRAW,
+ RAW_READ_LOCKREAD,
+ RAW_READ_READ,
+ RAW_READ_READX,
+ RAW_READ_SMB2
+};
+
+#define RAW_READ_GENERIC RAW_READ_READX
+
+/* union for read() backend call
+
+ note that .infoX.out.data will be allocated before the backend is
+ called. It will be big enough to hold the maximum size asked for
+*/
+union smb_read {
+ /* SMBreadX (and generic) interface */
+ struct {
+ enum smb_read_level level;
+ struct {
+ union smb_handle file;
+ uint64_t offset;
+ uint32_t mincnt; /* enforced on SMB2, 16 bit on SMB */
+ uint32_t maxcnt;
+ uint16_t remaining;
+ bool read_for_execute;
+ } in;
+ struct {
+ uint8_t *data;
+ uint16_t remaining;
+ uint16_t compaction_mode;
+ uint32_t nread;
+ } out;
+ } readx, generic;
+
+ /* SMBreadbraw interface */
+ struct {
+ enum smb_read_level level;
+ struct {
+ union smb_handle file;
+ uint64_t offset;
+ uint16_t maxcnt;
+ uint16_t mincnt;
+ uint32_t timeout;
+ } in;
+ struct {
+ uint8_t *data;
+ uint32_t nread;
+ } out;
+ } readbraw;
+
+
+ /* SMBlockandread interface */
+ struct {
+ enum smb_read_level level;
+ struct {
+ union smb_handle file;
+ uint16_t count;
+ uint32_t offset;
+ uint16_t remaining;
+ } in;
+ struct {
+ uint8_t *data;
+ uint16_t nread;
+ } out;
+ } lockread;
+
+ /* SMBread interface */
+ struct {
+ enum smb_read_level level;
+ struct {
+ union smb_handle file;
+ uint16_t count;
+ uint32_t offset;
+ uint16_t remaining;
+ } in;
+ struct {
+ uint8_t *data;
+ uint16_t nread;
+ } out;
+ } read;
+
+ /* SMB2 Read */
+ struct smb2_read {
+ enum smb_read_level level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 48 (0x30) bytes */
+ /* uint16_t buffer_code; 0x31 = 0x30 + 1 */
+ uint8_t _pad;
+ uint8_t reserved;
+ uint32_t length;
+ uint64_t offset;
+ /* struct smb2_handle handle; */
+ uint32_t min_count;
+ uint32_t channel;
+ uint32_t remaining;
+ /* the docs give no indication of what
+ these channel variables are for */
+ uint16_t channel_offset;
+ uint16_t channel_length;
+ } in;
+ struct {
+ /* static body buffer 16 (0x10) bytes */
+ /* uint16_t buffer_code; 0x11 = 0x10 + 1 */
+ /* uint8_t data_ofs; */
+ /* uint8_t reserved; */
+ /* uint32_t data_size; */
+ uint32_t remaining;
+ uint32_t reserved;
+
+ /* dynamic body */
+ DATA_BLOB data;
+ } out;
+ } smb2;
+};
+
+
+enum smb_write_level {
+ RAW_WRITE_WRITEUNLOCK,
+ RAW_WRITE_WRITE,
+ RAW_WRITE_WRITEX,
+ RAW_WRITE_WRITECLOSE,
+ RAW_WRITE_SPLWRITE,
+ RAW_WRITE_SMB2
+};
+
+#define RAW_WRITE_GENERIC RAW_WRITE_WRITEX
+
+/* union for write() backend call
+*/
+union smb_write {
+ /* SMBwriteX interface */
+ struct {
+ enum smb_write_level level;
+ struct {
+ union smb_handle file;
+ uint64_t offset;
+ uint16_t wmode;
+ uint16_t remaining;
+ uint32_t count;
+ const uint8_t *data;
+ } in;
+ struct {
+ uint32_t nwritten;
+ uint16_t remaining;
+ } out;
+ } writex, generic;
+
+ /* SMBwriteunlock interface */
+ struct {
+ enum smb_write_level level;
+ struct {
+ union smb_handle file;
+ uint16_t count;
+ uint32_t offset;
+ uint16_t remaining;
+ const uint8_t *data;
+ } in;
+ struct {
+ uint32_t nwritten;
+ } out;
+ } writeunlock;
+
+ /* SMBwrite interface */
+ struct {
+ enum smb_write_level level;
+ struct {
+ union smb_handle file;
+ uint16_t count;
+ uint32_t offset;
+ uint16_t remaining;
+ const uint8_t *data;
+ } in;
+ struct {
+ uint16_t nwritten;
+ } out;
+ } write;
+
+ /* SMBwriteclose interface */
+ struct {
+ enum smb_write_level level;
+ struct {
+ union smb_handle file;
+ uint16_t count;
+ uint32_t offset;
+ time_t mtime;
+ const uint8_t *data;
+ } in;
+ struct {
+ uint16_t nwritten;
+ } out;
+ } writeclose;
+
+ /* SMBsplwrite interface */
+ struct {
+ enum smb_write_level level;
+ struct {
+ union smb_handle file;
+ uint16_t count;
+ const uint8_t *data;
+ } in;
+ } splwrite;
+
+ /* SMB2 Write */
+ struct smb2_write {
+ enum smb_write_level level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 48 (0x30) bytes */
+ /* uint16_t buffer_code; 0x31 = 0x30 + 1 */
+ /* uint16_t data_ofs; */
+ /* uint32_t data_size; */
+ uint64_t offset;
+ /* struct smb2_handle handle; */
+ uint64_t unknown1; /* 0xFFFFFFFFFFFFFFFF */
+ uint64_t unknown2; /* 0xFFFFFFFFFFFFFFFF */
+
+ /* dynamic body */
+ DATA_BLOB data;
+ } in;
+ struct {
+ /* static body buffer 17 (0x11) bytes */
+ /* uint16_t buffer_code; 0x11 = 0x10 + 1*/
+ uint16_t _pad;
+ uint32_t nwritten;
+ uint64_t unknown1; /* 0x0000000000000000 */
+ } out;
+ } smb2;
+};
+
+
+enum smb_lock_level {
+ RAW_LOCK_LOCK,
+ RAW_LOCK_UNLOCK,
+ RAW_LOCK_LOCKX,
+ RAW_LOCK_SMB2,
+ RAW_LOCK_SMB2_BREAK
+};
+
+#define RAW_LOCK_GENERIC RAW_LOCK_LOCKX
+
+/* union for lock() backend call
+*/
+union smb_lock {
+ /* SMBlockingX and generic interface */
+ struct {
+ enum smb_lock_level level;
+ struct {
+ union smb_handle file;
+ uint16_t mode;
+ uint32_t timeout;
+ uint16_t ulock_cnt;
+ uint16_t lock_cnt;
+ struct smb_lock_entry {
+ uint32_t pid; /* 16 bits in SMB1 */
+ uint64_t offset;
+ uint64_t count;
+ } *locks; /* unlocks are first in the arrray */
+ } in;
+ } generic, lockx;
+
+ /* SMBlock and SMBunlock interface */
+ struct {
+ enum smb_lock_level level;
+ struct {
+ union smb_handle file;
+ uint32_t count;
+ uint32_t offset;
+ } in;
+ } lock, unlock;
+
+ /* SMB2 Lock */
+ struct smb2_lock {
+ enum smb_lock_level level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 48 (0x30) bytes */
+ /* uint16_t buffer_code; 0x30 */
+ uint16_t lock_count;
+ uint32_t reserved;
+ /* struct smb2_handle handle; */
+ struct smb2_lock_element {
+ uint64_t offset;
+ uint64_t length;
+/* these flags are the same as the SMB2 lock flags */
+#define SMB2_LOCK_FLAG_NONE 0x00000000
+#define SMB2_LOCK_FLAG_SHARED 0x00000001
+#define SMB2_LOCK_FLAG_EXCLUSIVE 0x00000002
+#define SMB2_LOCK_FLAG_UNLOCK 0x00000004
+#define SMB2_LOCK_FLAG_FAIL_IMMEDIATELY 0x00000010
+#define SMB2_LOCK_FLAG_ALL_MASK 0x00000017
+ uint32_t flags;
+ uint32_t reserved;
+ } *locks;
+ } in;
+ struct {
+ /* static body buffer 4 (0x04) bytes */
+ /* uint16_t buffer_code; 0x04 */
+ uint16_t reserved;
+ } out;
+ } smb2;
+
+ /* SMB2 Break */
+ struct smb2_break {
+ enum smb_lock_level level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 24 (0x18) bytes */
+ uint8_t oplock_level;
+ uint8_t reserved;
+ uint32_t reserved2;
+ /* struct smb2_handle handle; */
+ } in, out;
+ } smb2_break;
+};
+
+
+enum smb_close_level {
+ RAW_CLOSE_CLOSE,
+ RAW_CLOSE_SPLCLOSE,
+ RAW_CLOSE_SMB2,
+ RAW_CLOSE_GENERIC,
+};
+
+/*
+ union for close() backend call
+*/
+union smb_close {
+ /* generic interface */
+ struct {
+ enum smb_close_level level;
+ struct {
+ union smb_handle file;
+ time_t write_time;
+#define SMB2_CLOSE_FLAGS_FULL_INFORMATION (1<<0)
+ uint16_t flags; /* SMB2_CLOSE_FLAGS_* */
+ } in;
+ struct {
+ uint16_t flags;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t file_attr;
+ } out;
+ } generic;
+
+ /* SMBclose interface */
+ struct {
+ enum smb_close_level level;
+ struct {
+ union smb_handle file;
+ time_t write_time;
+ } in;
+ } close;
+
+ /* SMBsplclose interface - empty! */
+ struct {
+ enum smb_close_level level;
+ struct {
+ union smb_handle file;
+ } in;
+ } splclose;
+
+ /* SMB2 Close */
+ struct smb2_close {
+ enum smb_close_level level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 24 (0x18) bytes */
+ /* uint16_t buffer_code; 0x18 */
+ uint16_t flags; /* SMB2_CLOSE_FLAGS_* */
+ uint32_t _pad;
+ } in;
+ struct {
+ /* static body buffer 60 (0x3C) bytes */
+ /* uint16_t buffer_code; 0x3C */
+ uint16_t flags;
+ uint32_t _pad;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t file_attr;
+ } out;
+ } smb2;
+};
+
+
+enum smb_lpq_level {RAW_LPQ_GENERIC, RAW_LPQ_RETQ};
+
+/*
+ union for lpq() backend
+*/
+union smb_lpq {
+ /* generic interface */
+ struct {
+ enum smb_lpq_level level;
+
+ } generic;
+
+
+ /* SMBsplretq interface */
+ struct {
+ enum smb_lpq_level level;
+
+ struct {
+ uint16_t maxcount;
+ uint16_t startidx;
+ } in;
+ struct {
+ uint16_t count;
+ uint16_t restart_idx;
+ struct {
+ time_t time;
+ uint8_t status;
+ uint16_t job;
+ uint32_t size;
+ char *user;
+ } *queue;
+ } out;
+ } retq;
+};
+
+enum smb_ioctl_level {
+ RAW_IOCTL_IOCTL,
+ RAW_IOCTL_NTIOCTL,
+ RAW_IOCTL_SMB2,
+ RAW_IOCTL_SMB2_NO_HANDLE
+};
+
+/*
+ union for ioctl() backend
+*/
+union smb_ioctl {
+ /* generic interface */
+ struct {
+ enum smb_ioctl_level level;
+ struct {
+ union smb_handle file;
+ } in;
+ } generic;
+
+ /* struct for SMBioctl */
+ struct {
+ enum smb_ioctl_level level;
+ struct {
+ union smb_handle file;
+ uint32_t request;
+ } in;
+ struct {
+ DATA_BLOB blob;
+ } out;
+ } ioctl;
+
+
+ /* struct for NT ioctl call */
+ struct {
+ enum smb_ioctl_level level;
+ struct {
+ union smb_handle file;
+ uint32_t function;
+ bool fsctl;
+ uint8_t filter;
+ uint32_t max_data;
+ DATA_BLOB blob;
+ } in;
+ struct {
+ DATA_BLOB blob;
+ } out;
+ } ntioctl;
+
+ /* SMB2 Ioctl */
+ struct smb2_ioctl {
+ enum smb_ioctl_level level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 56 (0x38) bytes */
+ /* uint16_t buffer_code; 0x39 = 0x38 + 1 */
+ uint16_t _pad;
+ uint32_t function;
+ /*struct smb2_handle handle;*/
+ /* uint32_t out_ofs; */
+ /* uint32_t out_size; */
+ uint32_t unknown2;
+ /* uint32_t in_ofs; */
+ /* uint32_t in_size; */
+ uint32_t max_response_size;
+ uint64_t flags;
+
+ /* dynamic body */
+ DATA_BLOB out;
+ DATA_BLOB in;
+ } in;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 48 (0x30) bytes */
+ /* uint16_t buffer_code; 0x31 = 0x30 + 1 */
+ uint16_t _pad;
+ uint32_t function;
+ /* struct smb2_handle handle; */
+ /* uint32_t in_ofs; */
+ /* uint32_t in_size; */
+ /* uint32_t out_ofs; */
+ /* uint32_t out_size; */
+ uint32_t unknown2;
+ uint32_t unknown3;
+
+ /* dynamic body */
+ DATA_BLOB in;
+ DATA_BLOB out;
+ } out;
+ } smb2;
+};
+
+enum smb_flush_level {
+ RAW_FLUSH_FLUSH,
+ RAW_FLUSH_ALL,
+ RAW_FLUSH_SMB2
+};
+
+union smb_flush {
+ /* struct for SMBflush */
+ struct {
+ enum smb_flush_level level;
+ struct {
+ union smb_handle file;
+ } in;
+ } flush, generic;
+
+ /* SMBflush with 0xFFFF wildcard fnum */
+ struct {
+ enum smb_flush_level level;
+ } flush_all;
+
+ /* SMB2 Flush */
+ struct smb2_flush {
+ enum smb_flush_level level;
+ struct {
+ union smb_handle file;
+ uint16_t reserved1;
+ uint32_t reserved2;
+ } in;
+ struct {
+ uint16_t reserved;
+ } out;
+ } smb2;
+};
+
+/* struct for SMBcopy */
+struct smb_copy {
+ struct {
+ uint16_t tid2;
+ uint16_t ofun;
+ uint16_t flags;
+ const char *path1;
+ const char *path2;
+ } in;
+ struct {
+ uint16_t count;
+ } out;
+};
+
+
+/* struct for transact/transact2 call */
+struct smb_trans2 {
+ struct {
+ uint16_t max_param;
+ uint16_t max_data;
+ uint8_t max_setup;
+ uint16_t flags;
+ uint32_t timeout;
+ uint8_t setup_count;
+ uint16_t *setup;
+ const char *trans_name; /* SMBtrans only */
+ DATA_BLOB params;
+ DATA_BLOB data;
+ } in;
+
+ struct {
+ uint8_t setup_count;
+ uint16_t *setup;
+ DATA_BLOB params;
+ DATA_BLOB data;
+ } out;
+};
+
+/* struct for nttransact2 call */
+struct smb_nttrans {
+ struct {
+ uint8_t max_setup;
+ uint32_t max_param;
+ uint32_t max_data;
+ uint8_t setup_count;
+ uint16_t function;
+ uint8_t *setup;
+ DATA_BLOB params;
+ DATA_BLOB data;
+ } in;
+
+ struct {
+ uint8_t setup_count; /* in units of 16 bit words */
+ uint8_t *setup;
+ DATA_BLOB params;
+ DATA_BLOB data;
+ } out;
+};
+
+enum smb_notify_level {
+ RAW_NOTIFY_NTTRANS,
+ RAW_NOTIFY_SMB2
+};
+
+union smb_notify {
+ /* struct for nttrans change notify call */
+ struct {
+ enum smb_notify_level level;
+
+ struct {
+ union smb_handle file;
+ uint32_t buffer_size;
+ uint32_t completion_filter;
+ bool recursive;
+ } in;
+
+ struct {
+ uint32_t num_changes;
+ struct notify_changes {
+ uint32_t action;
+ struct smb_wire_string name;
+ } *changes;
+ } out;
+ } nttrans;
+
+ struct smb2_notify {
+ enum smb_notify_level level;
+
+ struct {
+ union smb_handle file;
+ /* static body buffer 32 (0x20) bytes */
+ /* uint16_t buffer_code; 0x32 */
+ uint16_t recursive;
+ uint32_t buffer_size;
+ /*struct smb2_handle file;*/
+ uint32_t completion_filter;
+ uint32_t unknown;
+ } in;
+
+ struct {
+ /* static body buffer 8 (0x08) bytes */
+ /* uint16_t buffer_code; 0x09 = 0x08 + 1 */
+ /* uint16_t blob_ofs; */
+ /* uint16_t blob_size; */
+
+ /* dynamic body */
+ /*DATA_BLOB blob;*/
+
+ /* DATA_BLOB content */
+ uint32_t num_changes;
+ struct notify_changes *changes;
+ } out;
+ } smb2;
+};
+
+enum smb_search_level {
+ RAW_SEARCH_SEARCH, /* SMBsearch */
+ RAW_SEARCH_FFIRST, /* SMBffirst */
+ RAW_SEARCH_FUNIQUE, /* SMBfunique */
+ RAW_SEARCH_TRANS2, /* SMBtrans2 */
+ RAW_SEARCH_SMB2 /* SMB2 Find */
+};
+
+enum smb_search_data_level {
+ RAW_SEARCH_DATA_GENERIC = 0x10000, /* only used in the smbcli_ code */
+ RAW_SEARCH_DATA_SEARCH,
+ RAW_SEARCH_DATA_STANDARD = SMB_FIND_STANDARD,
+ RAW_SEARCH_DATA_EA_SIZE = SMB_FIND_EA_SIZE,
+ RAW_SEARCH_DATA_EA_LIST = SMB_FIND_EA_LIST,
+ RAW_SEARCH_DATA_DIRECTORY_INFO = SMB_FIND_DIRECTORY_INFO,
+ RAW_SEARCH_DATA_FULL_DIRECTORY_INFO = SMB_FIND_FULL_DIRECTORY_INFO,
+ RAW_SEARCH_DATA_NAME_INFO = SMB_FIND_NAME_INFO,
+ RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO = SMB_FIND_BOTH_DIRECTORY_INFO,
+ RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO = SMB_FIND_ID_FULL_DIRECTORY_INFO,
+ RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO = SMB_FIND_ID_BOTH_DIRECTORY_INFO,
+ RAW_SEARCH_DATA_UNIX_INFO = SMB_FIND_UNIX_INFO,
+ RAW_SEARCH_DATA_UNIX_INFO2 = SMB_FIND_UNIX_INFO2
+};
+
+/* union for file search */
+union smb_search_first {
+ struct {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+ } generic;
+
+ /* search (old) findfirst interface.
+ Also used for ffirst and funique. */
+ struct {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+
+ struct {
+ uint16_t max_count;
+ uint16_t search_attrib;
+ const char *pattern;
+ } in;
+ struct {
+ int16_t count;
+ } out;
+ } search_first;
+
+ /* trans2 findfirst interface */
+ struct {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+
+ struct {
+ uint16_t search_attrib;
+ uint16_t max_count;
+ uint16_t flags;
+ uint32_t storage_type;
+ const char *pattern;
+
+ /* the ea names are only used for RAW_SEARCH_EA_LIST */
+ uint_t num_names;
+ struct ea_name *ea_names;
+ } in;
+ struct {
+ uint16_t handle;
+ uint16_t count;
+ uint16_t end_of_search;
+ } out;
+ } t2ffirst;
+
+/*
+ SMB2 uses different level numbers for the same old SMB trans2 search levels
+*/
+#define SMB2_FIND_DIRECTORY_INFO 0x01
+#define SMB2_FIND_FULL_DIRECTORY_INFO 0x02
+#define SMB2_FIND_BOTH_DIRECTORY_INFO 0x03
+#define SMB2_FIND_NAME_INFO 0x0C
+#define SMB2_FIND_ID_BOTH_DIRECTORY_INFO 0x25
+#define SMB2_FIND_ID_FULL_DIRECTORY_INFO 0x26
+
+/* flags for SMB2 find */
+#define SMB2_CONTINUE_FLAG_RESTART 0x01
+#define SMB2_CONTINUE_FLAG_SINGLE 0x02
+#define SMB2_CONTINUE_FLAG_INDEX 0x04
+#define SMB2_CONTINUE_FLAG_REOPEN 0x10
+
+ /* SMB2 Find */
+ struct smb2_find {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 32 (0x20) bytes */
+ /* uint16_t buffer_code; 0x21 = 0x20 + 1 */
+ uint8_t level;
+ uint8_t continue_flags; /* SMB2_CONTINUE_FLAG_* */
+ uint32_t file_index;
+ /* struct smb2_handle handle; */
+ /* uint16_t pattern_ofs; */
+ /* uint16_t pattern_size; */
+ uint32_t max_response_size;
+
+ /* dynamic body */
+ const char *pattern;
+ } in;
+ struct {
+ /* static body buffer 8 (0x08) bytes */
+ /* uint16_t buffer_code; 0x08 */
+ /* uint16_t blob_ofs; */
+ /* uint32_t blob_size; */
+
+ /* dynamic body */
+ DATA_BLOB blob;
+ } out;
+ } smb2;
+};
+
+/* union for file search continue */
+union smb_search_next {
+ struct {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+ } generic;
+
+ /* search (old) findnext interface. Also used
+ for ffirst when continuing */
+ struct {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+
+ struct {
+ uint16_t max_count;
+ uint16_t search_attrib;
+ struct smb_search_id {
+ uint8_t reserved;
+ char name[11];
+ uint8_t handle;
+ uint32_t server_cookie;
+ uint32_t client_cookie;
+ } id;
+ } in;
+ struct {
+ uint16_t count;
+ } out;
+ } search_next;
+
+ /* trans2 findnext interface */
+ struct {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+
+ struct {
+ uint16_t handle;
+ uint16_t max_count;
+ uint32_t resume_key;
+ uint16_t flags;
+ const char *last_name;
+
+ /* the ea names are only used for RAW_SEARCH_EA_LIST */
+ uint_t num_names;
+ struct ea_name *ea_names;
+ } in;
+ struct {
+ uint16_t count;
+ uint16_t end_of_search;
+ } out;
+ } t2fnext;
+
+ /* SMB2 Find */
+ struct smb2_find smb2;
+};
+
+/* union for search reply file data */
+union smb_search_data {
+ /*
+ * search (old) findfirst
+ * RAW_SEARCH_DATA_SEARCH
+ */
+ struct {
+ uint16_t attrib;
+ time_t write_time;
+ uint32_t size;
+ struct smb_search_id id;
+ const char *name;
+ } search;
+
+ /* trans2 findfirst RAW_SEARCH_DATA_STANDARD level */
+ struct {
+ uint32_t resume_key;
+ time_t create_time;
+ time_t access_time;
+ time_t write_time;
+ uint32_t size;
+ uint32_t alloc_size;
+ uint16_t attrib;
+ struct smb_wire_string name;
+ } standard;
+
+ /* trans2 findfirst RAW_SEARCH_DATA_EA_SIZE level */
+ struct {
+ uint32_t resume_key;
+ time_t create_time;
+ time_t access_time;
+ time_t write_time;
+ uint32_t size;
+ uint32_t alloc_size;
+ uint16_t attrib;
+ uint32_t ea_size;
+ struct smb_wire_string name;
+ } ea_size;
+
+ /* trans2 findfirst RAW_SEARCH_DATA_EA_LIST level */
+ struct {
+ uint32_t resume_key;
+ time_t create_time;
+ time_t access_time;
+ time_t write_time;
+ uint32_t size;
+ uint32_t alloc_size;
+ uint16_t attrib;
+ struct smb_ea_list eas;
+ struct smb_wire_string name;
+ } ea_list;
+
+ /* RAW_SEARCH_DATA_DIRECTORY_INFO interface */
+ struct {
+ uint32_t file_index;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t size;
+ uint64_t alloc_size;
+ uint32_t attrib;
+ struct smb_wire_string name;
+ } directory_info;
+
+ /* RAW_SEARCH_DATA_FULL_DIRECTORY_INFO interface */
+ struct {
+ uint32_t file_index;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t size;
+ uint64_t alloc_size;
+ uint32_t attrib;
+ uint32_t ea_size;
+ struct smb_wire_string name;
+ } full_directory_info;
+
+ /* RAW_SEARCH_DATA_NAME_INFO interface */
+ struct {
+ uint32_t file_index;
+ struct smb_wire_string name;
+ } name_info;
+
+ /* RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO interface */
+ struct {
+ uint32_t file_index;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t size;
+ uint64_t alloc_size;
+ uint32_t attrib;
+ uint32_t ea_size;
+ struct smb_wire_string short_name;
+ struct smb_wire_string name;
+ } both_directory_info;
+
+ /* RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO interface */
+ struct {
+ uint32_t file_index;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t size;
+ uint64_t alloc_size;
+ uint32_t attrib;
+ uint32_t ea_size;
+ uint64_t file_id;
+ struct smb_wire_string name;
+ } id_full_directory_info;
+
+ /* RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO interface */
+ struct {
+ uint32_t file_index;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t size;
+ uint64_t alloc_size;
+ uint32_t attrib;
+ uint32_t ea_size;
+ uint64_t file_id;
+ struct smb_wire_string short_name;
+ struct smb_wire_string name;
+ } id_both_directory_info;
+
+ /* RAW_SEARCH_DATA_UNIX_INFO interface */
+ struct {
+ uint32_t file_index;
+ uint64_t size;
+ uint64_t alloc_size;
+ NTTIME status_change_time;
+ NTTIME access_time;
+ NTTIME change_time;
+ uint64_t uid;
+ uint64_t gid;
+ uint32_t file_type;
+ uint64_t dev_major;
+ uint64_t dev_minor;
+ uint64_t unique_id;
+ uint64_t permissions;
+ uint64_t nlink;
+ const char *name;
+ } unix_info;
+
+ /* RAW_SEARCH_DATA_UNIX_INFO2 interface */
+ struct {
+ uint32_t file_index;
+ uint64_t end_of_file;
+ uint64_t num_bytes;
+ NTTIME status_change_time;
+ NTTIME access_time;
+ NTTIME change_time;
+ uint64_t uid;
+ uint64_t gid;
+ uint32_t file_type;
+ uint64_t dev_major;
+ uint64_t dev_minor;
+ uint64_t unique_id;
+ uint64_t permissions;
+ uint64_t nlink;
+ NTTIME create_time;
+ uint32_t file_flags;
+ uint32_t flags_mask;
+ struct smb_wire_string name;
+ } unix_info2;
+};
+
+/* Callback function passed to the raw search interface. */
+typedef bool (*smbcli_search_callback)(void *private_data, const union smb_search_data *file);
+
+enum smb_search_close_level {RAW_FINDCLOSE_GENERIC, RAW_FINDCLOSE_FCLOSE, RAW_FINDCLOSE_FINDCLOSE};
+
+/* union for file search close */
+union smb_search_close {
+ struct {
+ enum smb_search_close_level level;
+ } generic;
+
+ /* SMBfclose (old search) interface */
+ struct {
+ enum smb_search_close_level level;
+
+ struct {
+ /* max_count and search_attrib are not used, but are present */
+ uint16_t max_count;
+ uint16_t search_attrib;
+ struct smb_search_id id;
+ } in;
+ } fclose;
+
+ /* SMBfindclose interface */
+ struct {
+ enum smb_search_close_level level;
+
+ struct {
+ uint16_t handle;
+ } in;
+ } findclose;
+};
+
+
+/*
+ struct for SMBecho call
+*/
+struct smb_echo {
+ struct {
+ uint16_t repeat_count;
+ uint16_t size;
+ uint8_t *data;
+ } in;
+ struct {
+ uint16_t count;
+ uint16_t sequence_number;
+ uint16_t size;
+ uint8_t *data;
+ } out;
+};
+
+/*
+ struct for shadow copy volumes
+ */
+struct smb_shadow_copy {
+ struct {
+ union smb_handle file;
+ uint32_t max_data;
+ } in;
+ struct {
+ uint32_t num_volumes;
+ uint32_t num_names;
+ const char **names;
+ } out;
+};
+
+#endif /* __LIBCLI_RAW_INTERFACES_H__ */
diff --git a/source4/libcli/raw/ioctl.h b/source4/libcli/raw/ioctl.h
new file mode 100644
index 0000000000..a9d3d1b7a6
--- /dev/null
+++ b/source4/libcli/raw/ioctl.h
@@ -0,0 +1,59 @@
+/*
+ Unix SMB/CIFS implementation.
+ ioctl and fsctl definitions
+
+ Copyright (C) Andrew Tridgell 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/>.
+*/
+
+
+/* ioctl codes */
+#define IOCTL_QUERY_JOB_INFO 0x530060
+
+
+/* filesystem control codes */
+#define FSCTL_METHOD_BUFFERED 0x00000000
+#define FSCTL_METHOD_IN_DIRECT 0x00000001
+#define FSCTL_METHOD_OUT_DIRECT 0x00000002
+#define FSCTL_METHOD_NEITHER 0x00000003
+
+#define FSCTL_ACCESS_ANY 0x00000000
+#define FSCTL_ACCESS_READ 0x00004000
+#define FSCTL_ACCESS_WRITE 0x00008000
+
+#define FSCTL_FILESYSTEM 0x00090000
+#define FSCTL_REQUEST_OPLOCK_LEVEL_1 (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0000 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_REQUEST_OPLOCK_LEVEL_2 (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0004 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_REQUEST_BATCH_OPLOCK (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0008 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_OPLOCK_BREAK_ACKNOWLEDGE (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x000C | FSCTL_METHOD_BUFFERED)
+#define FSCTL_OPBATCH_ACK_CLOSE_PENDING (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0010 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_OPLOCK_BREAK_NOTIFY (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0014 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_FILESYS_GET_STATISTICS (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0060 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_GET_NTFS_VOLUME_DATA (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0064 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_FIND_FILES_BY_SID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x008C | FSCTL_METHOD_NEITHER)
+#define FSCTL_SET_OBJECT_ID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0098 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_GET_OBJECT_ID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x009C | FSCTL_METHOD_BUFFERED)
+#define FSCTL_DELETE_OBJECT_ID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00A0 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_SET_REPARSE_POINT (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00A4 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_GET_REPARSE_POINT (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00A8 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_DELETE_REPARSE_POINT (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00AC | FSCTL_METHOD_BUFFERED)
+#define FSCTL_CREATE_OR_GET_OBJECT_ID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00C0 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_SET_SPARSE (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00C4 | FSCTL_METHOD_BUFFERED)
+
+#define FSCTL_NAMED_PIPE 0x00110000
+#define FSCTL_NAMED_PIPE_READ_WRITE (FSCTL_NAMED_PIPE | FSCTL_ACCESS_ANY | 0xC014 | FSCTL_METHOD_NEITHER)
+
+#define FSCTL_NETWORK_FILESYSTEM 0x00140000
+#define FSCTL_GET_SHADOW_COPY_DATA (FSCTL_NETWORK_FILESYSTEM | FSCTL_ACCESS_READ | 0x0064 | FSCTL_METHOD_BUFFERED)
diff --git a/source4/libcli/raw/libcliraw.h b/source4/libcli/raw/libcliraw.h
new file mode 100644
index 0000000000..a9fcdab9cc
--- /dev/null
+++ b/source4/libcli/raw/libcliraw.h
@@ -0,0 +1,384 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+
+ Copyright (C) Andrew Tridgell 2002-2004
+ Copyright (C) James Myers 2003 <myersjj@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/>.
+*/
+
+#ifndef __LIBCLI_RAW_H__
+#define __LIBCLI_RAW_H__
+
+#include "libcli/raw/request.h"
+#include "librpc/gen_ndr/nbt.h"
+
+struct smbcli_tree; /* forward declare */
+struct smbcli_request; /* forward declare */
+struct smbcli_session; /* forward declare */
+struct smbcli_transport; /* forward declare */
+
+struct resolve_context;
+struct cli_credentials;
+struct gensec_settings;
+
+/* default timeout for all smb requests */
+#define SMB_REQUEST_TIMEOUT 60
+
+/* context that will be and has been negotiated between the client and server */
+struct smbcli_negotiate {
+ /*
+ * negotiated maximum transmit size - this is given to us by the server
+ */
+ uint32_t max_xmit;
+
+ /* maximum number of requests that can be multiplexed */
+ uint16_t max_mux;
+
+ /* the negotiatiated protocol */
+ enum protocol_types protocol;
+
+ uint8_t sec_mode; /* security mode returned by negprot */
+ uint8_t key_len;
+ DATA_BLOB server_guid; /* server_guid */
+ DATA_BLOB secblob; /* cryptkey or negTokenInit blob */
+ uint32_t sesskey;
+
+ struct smb_signing_context sign_info;
+
+ /* capabilities that the server reported */
+ uint32_t capabilities;
+
+ int server_zone;
+ time_t server_time;
+ uint_t readbraw_supported:1;
+ uint_t writebraw_supported:1;
+
+ char *server_domain;
+};
+
+/* this is the context for a SMB socket associated with the socket itself */
+struct smbcli_socket {
+ struct socket_context *sock;
+
+ /* what port we ended up connected to */
+ int port;
+
+ /* the hostname we connected to */
+ const char *hostname;
+
+ /* the event handle for waiting for socket IO */
+ struct {
+ struct tevent_context *ctx;
+ struct tevent_fd *fde;
+ struct tevent_timer *te;
+ } event;
+};
+
+/*
+ this structure allows applications to control the behaviour of the
+ client library
+*/
+struct smbcli_options {
+ uint_t use_oplocks:1;
+ uint_t use_level2_oplocks:1;
+ uint_t use_spnego:1;
+ uint_t unicode:1;
+ uint_t ntstatus_support:1;
+ int max_protocol;
+ uint32_t max_xmit;
+ uint16_t max_mux;
+ int request_timeout;
+ enum smb_signing_state signing;
+};
+
+/* this is the context for the client transport layer */
+struct smbcli_transport {
+ /* socket level info */
+ struct smbcli_socket *socket;
+
+ /* the next mid to be allocated - needed for signing and
+ request matching */
+ uint16_t next_mid;
+
+ /* negotiated protocol information */
+ struct smbcli_negotiate negotiate;
+
+ /* options to control the behaviour of the client code */
+ struct smbcli_options options;
+
+ /* is a readbraw pending? we need to handle that case
+ specially on receiving packets */
+ uint_t readbraw_pending:1;
+
+ /* an idle function - if this is defined then it will be
+ called once every period microseconds while we are waiting
+ for a packet */
+ struct {
+ void (*func)(struct smbcli_transport *, void *);
+ void *private_data;
+ uint_t period;
+ } idle;
+
+ /* the error fields from the last message */
+ struct {
+ enum {ETYPE_NONE, ETYPE_SMB, ETYPE_SOCKET, ETYPE_NBT} etype;
+ union {
+ NTSTATUS nt_status;
+ enum {SOCKET_READ_TIMEOUT,
+ SOCKET_READ_EOF,
+ SOCKET_READ_ERROR,
+ SOCKET_WRITE_ERROR,
+ SOCKET_READ_BAD_SIG} socket_error;
+ uint_t nbt_error;
+ } e;
+ } error;
+
+ struct {
+ /* a oplock break request handler */
+ bool (*handler)(struct smbcli_transport *transport,
+ uint16_t tid, uint16_t fnum, uint8_t level, void *private_data);
+ /* private data passed to the oplock handler */
+ void *private_data;
+ } oplock;
+
+ /* a list of async requests that are pending for receive on this connection */
+ struct smbcli_request *pending_recv;
+
+ /* remember the called name - some sub-protocols require us to
+ know the server name */
+ struct nbt_name called;
+
+ /* context of the stream -> packet parser */
+ struct packet_context *packet;
+
+ /* iconv convenience */
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+/* this is the context for the user */
+
+/* this is the context for the session layer */
+struct smbcli_session {
+ /* transport layer info */
+ struct smbcli_transport *transport;
+
+ /* after a session setup the server provides us with
+ a vuid identifying the security context */
+ uint16_t vuid;
+
+ /* default pid for this session */
+ uint32_t pid;
+
+ /* the flags2 for each packet - this allows
+ the user to control these for torture testing */
+ uint16_t flags2;
+
+ DATA_BLOB user_session_key;
+
+ /* the spnego context if we use extented security */
+ struct gensec_security *gensec;
+
+ struct smbcli_session_options {
+ uint_t lanman_auth:1;
+ uint_t ntlmv2_auth:1;
+ uint_t plaintext_auth:1;
+ } options;
+};
+
+/*
+ smbcli_tree context: internal state for a tree connection.
+ */
+struct smbcli_tree {
+ /* session layer info */
+ struct smbcli_session *session;
+
+ uint16_t tid; /* tree id, aka cnum */
+ char *device;
+ char *fs_type;
+};
+
+
+/*
+ a client request moves between the following 4 states.
+*/
+enum smbcli_request_state {SMBCLI_REQUEST_INIT, /* we are creating the request */
+ SMBCLI_REQUEST_RECV, /* we are waiting for a matching reply */
+ SMBCLI_REQUEST_DONE, /* the request is finished */
+ SMBCLI_REQUEST_ERROR}; /* a packet or transport level error has occurred */
+
+/* the context for a single SMB request. This is passed to any request-context
+ * functions (similar to context.h, the server version).
+ * This will allow requests to be multi-threaded. */
+struct smbcli_request {
+ /* allow a request to be part of a list of requests */
+ struct smbcli_request *next, *prev;
+
+ /* each request is in one of 4 possible states */
+ enum smbcli_request_state state;
+
+ /* a request always has a transport context, nearly always has
+ a session context and usually has a tree context */
+ struct smbcli_transport *transport;
+ struct smbcli_session *session;
+ struct smbcli_tree *tree;
+
+ /* a receive helper, smbcli_transport_finish_recv will not call
+ req->async.fn callback handler unless the recv_helper returns
+ a value > SMBCLI_REQUEST_RECV. */
+ struct {
+ enum smbcli_request_state (*fn)(struct smbcli_request *);
+ void *private_data;
+ } recv_helper;
+
+ /* the flags2 from the SMB request, in raw form (host byte
+ order). Used to parse strings */
+ uint16_t flags2;
+
+ /* the NT status for this request. Set by packet receive code
+ or code detecting error. */
+ NTSTATUS status;
+
+ /* the sequence number of this packet - used for signing */
+ uint_t seq_num;
+
+ /* list of ntcancel request for this requests */
+ struct smbcli_request *ntcancel;
+
+ /* set if this is a one-way request, meaning we are not
+ expecting a reply from the server. */
+ uint_t one_way_request:1;
+
+ /* set this when the request should only increment the signing
+ counter by one */
+ uint_t sign_single_increment:1;
+
+ /* the caller wants to do the signing check */
+ bool sign_caller_checks;
+
+ /* give the caller a chance to prevent the talloc_free() in the _recv() function */
+ bool do_not_free;
+
+ /* the mid of this packet - used to match replies */
+ uint16_t mid;
+
+ struct smb_request_buffer in;
+ struct smb_request_buffer out;
+
+ /* information on what to do with a reply when it is received
+ asyncronously. If this is not setup when a reply is received then
+ the reply is discarded
+
+ The private pointer is private to the caller of the client
+ library (the application), not private to the library
+ */
+ struct {
+ void (*fn)(struct smbcli_request *);
+ void *private_data;
+ } async;
+};
+
+/* useful way of catching wct errors with file and line number */
+#define SMBCLI_CHECK_MIN_WCT(req, wcount) if ((req)->in.wct < (wcount)) { \
+ DEBUG(1,("Unexpected WCT %d at %s(%d) - expected min %d\n", (req)->in.wct, __FILE__, __LINE__, wcount)); \
+ req->status = NT_STATUS_INVALID_PARAMETER; \
+ goto failed; \
+}
+
+#define SMBCLI_CHECK_WCT(req, wcount) if ((req)->in.wct != (wcount)) { \
+ DEBUG(1,("Unexpected WCT %d at %s(%d) - expected %d\n", (req)->in.wct, __FILE__, __LINE__, wcount)); \
+ req->status = NT_STATUS_INVALID_PARAMETER; \
+ goto failed; \
+}
+
+#include "libcli/raw/interfaces.h"
+
+NTSTATUS smb_raw_read_recv(struct smbcli_request *req, union smb_read *parms);
+struct smbcli_request *smb_raw_read_send(struct smbcli_tree *tree, union smb_read *parms);
+NTSTATUS smb_raw_trans_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms);
+size_t smb_raw_max_trans_data(struct smbcli_tree *tree, size_t param_size);
+struct smbcli_request *smb_raw_trans_send(struct smbcli_tree *tree, struct smb_trans2 *parms);
+NTSTATUS smbcli_request_destroy(struct smbcli_request *req);
+struct smbcli_request *smb_raw_write_send(struct smbcli_tree *tree, union smb_write *parms);
+struct smbcli_request *smb_raw_close_send(struct smbcli_tree *tree, union smb_close *parms);
+NTSTATUS smb_raw_open_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms);
+struct smbcli_request *smb_raw_open_send(struct smbcli_tree *tree, union smb_open *parms);
+
+bool smbcli_transport_process(struct smbcli_transport *transport);
+const char *smbcli_errstr(struct smbcli_tree *tree);
+NTSTATUS smb_raw_fsinfo(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_fsinfo *fsinfo);
+NTSTATUS smb_raw_pathinfo(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_fileinfo *parms);
+NTSTATUS smb_raw_shadow_data(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, struct smb_shadow_copy *info);
+NTSTATUS smb_raw_fileinfo(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_fileinfo *parms);
+struct smbcli_tree *smbcli_tree_init(struct smbcli_session *session, TALLOC_CTX *parent_ctx, bool primary);
+NTSTATUS smb_raw_tcon(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_tcon *parms);
+void smbcli_oplock_handler(struct smbcli_transport *transport,
+ bool (*handler)(struct smbcli_transport *, uint16_t, uint16_t, uint8_t, void *),
+ void *private_data);
+void smbcli_transport_idle_handler(struct smbcli_transport *transport,
+ void (*idle_func)(struct smbcli_transport *, void *),
+ uint64_t period,
+ void *private_data);
+NTSTATUS smbcli_request_simple_recv(struct smbcli_request *req);
+bool smbcli_oplock_ack(struct smbcli_tree *tree, uint16_t fnum, uint16_t ack_level);
+NTSTATUS smb_raw_open(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_open *parms);
+NTSTATUS smb_raw_close(struct smbcli_tree *tree, union smb_close *parms);
+NTSTATUS smb_raw_unlink(struct smbcli_tree *tree, union smb_unlink *parms);
+NTSTATUS smb_raw_chkpath(struct smbcli_tree *tree, union smb_chkpath *parms);
+NTSTATUS smb_raw_mkdir(struct smbcli_tree *tree, union smb_mkdir *parms);
+NTSTATUS smb_raw_rmdir(struct smbcli_tree *tree, struct smb_rmdir *parms);
+NTSTATUS smb_raw_rename(struct smbcli_tree *tree, union smb_rename *parms);
+NTSTATUS smb_raw_seek(struct smbcli_tree *tree, union smb_seek *parms);
+NTSTATUS smb_raw_read(struct smbcli_tree *tree, union smb_read *parms);
+NTSTATUS smb_raw_write(struct smbcli_tree *tree, union smb_write *parms);
+NTSTATUS smb_raw_lock(struct smbcli_tree *tree, union smb_lock *parms);
+NTSTATUS smb_raw_setpathinfo(struct smbcli_tree *tree, union smb_setfileinfo *parms);
+NTSTATUS smb_raw_setfileinfo(struct smbcli_tree *tree, union smb_setfileinfo *parms);
+
+struct smbcli_request *smb_raw_changenotify_send(struct smbcli_tree *tree, union smb_notify *parms);
+NTSTATUS smb_raw_changenotify_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_notify *parms);
+
+NTSTATUS smb_tree_disconnect(struct smbcli_tree *tree);
+NTSTATUS smbcli_nt_error(struct smbcli_tree *tree);
+NTSTATUS smb_raw_exit(struct smbcli_session *session);
+NTSTATUS smb_raw_pathinfo_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms);
+struct smbcli_request *smb_raw_pathinfo_send(struct smbcli_tree *tree,
+ union smb_fileinfo *parms);
+struct smbcli_request *smb_raw_setpathinfo_send(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms);
+struct smbcli_request *smb_raw_echo_send(struct smbcli_transport *transport,
+ struct smb_echo *p);
+NTSTATUS smb_raw_search_first(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_first *io, void *private_data,
+ smbcli_search_callback callback);
+NTSTATUS smb_raw_flush(struct smbcli_tree *tree, union smb_flush *parms);
+
+NTSTATUS smb_raw_trans(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms);
+
+struct smbcli_socket *smbcli_sock_connect_byname(const char *host, const char **ports,
+ TALLOC_CTX *mem_ctx,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ const char *socket_options);
+void smbcli_sock_dead(struct smbcli_socket *sock);
+
+#endif /* __LIBCLI_RAW__H__ */
diff --git a/source4/libcli/raw/rawacl.c b/source4/libcli/raw/rawacl.c
new file mode 100644
index 0000000000..e13ba85361
--- /dev/null
+++ b/source4/libcli/raw/rawacl.c
@@ -0,0 +1,163 @@
+/*
+ Unix SMB/CIFS implementation.
+ ACL get/set operations
+
+ Copyright (C) Andrew Tridgell 2003-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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+/****************************************************************************
+fetch file ACL (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_query_secdesc_send(struct smbcli_tree *tree,
+ union smb_fileinfo *io)
+{
+ struct smb_nttrans nt;
+ uint8_t params[8];
+
+ nt.in.max_setup = 0;
+ nt.in.max_param = 4;
+ nt.in.max_data = 0xFFFF;
+ nt.in.setup_count = 0;
+ nt.in.function = NT_TRANSACT_QUERY_SECURITY_DESC;
+ nt.in.setup = NULL;
+
+ SSVAL(params, 0, io->query_secdesc.in.file.fnum);
+ SSVAL(params, 2, 0); /* padding */
+ SIVAL(params, 4, io->query_secdesc.in.secinfo_flags);
+
+ nt.in.params.data = params;
+ nt.in.params.length = 8;
+
+ nt.in.data = data_blob(NULL, 0);
+
+ return smb_raw_nttrans_send(tree, &nt);
+}
+
+
+/****************************************************************************
+fetch file ACL (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_query_secdesc_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *io)
+{
+ NTSTATUS status;
+ struct smb_nttrans nt;
+ struct ndr_pull *ndr;
+ enum ndr_err_code ndr_err;
+
+ status = smb_raw_nttrans_recv(req, mem_ctx, &nt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* check that the basics are valid */
+ if (nt.out.params.length != 4 ||
+ IVAL(nt.out.params.data, 0) > nt.out.data.length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt.out.data.length = IVAL(nt.out.params.data, 0);
+
+ ndr = ndr_pull_init_blob(&nt.out.data, mem_ctx, NULL);
+ if (!ndr) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ io->query_secdesc.out.sd = talloc(mem_ctx, struct security_descriptor);
+ if (!io->query_secdesc.out.sd) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ndr_err = ndr_pull_security_descriptor(ndr, NDR_SCALARS|NDR_BUFFERS,
+ io->query_secdesc.out.sd);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+fetch file ACL (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_query_secdesc(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *io)
+{
+ struct smbcli_request *req = smb_raw_query_secdesc_send(tree, io);
+ return smb_raw_query_secdesc_recv(req, mem_ctx, io);
+}
+
+
+
+/****************************************************************************
+set file ACL (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_set_secdesc_send(struct smbcli_tree *tree,
+ union smb_setfileinfo *io)
+{
+ struct smb_nttrans nt;
+ uint8_t params[8];
+ struct ndr_push *ndr;
+ struct smbcli_request *req;
+ enum ndr_err_code ndr_err;
+
+ nt.in.max_setup = 0;
+ nt.in.max_param = 0;
+ nt.in.max_data = 0;
+ nt.in.setup_count = 0;
+ nt.in.function = NT_TRANSACT_SET_SECURITY_DESC;
+ nt.in.setup = NULL;
+
+ SSVAL(params, 0, io->set_secdesc.in.file.fnum);
+ SSVAL(params, 2, 0); /* padding */
+ SIVAL(params, 4, io->set_secdesc.in.secinfo_flags);
+
+ nt.in.params.data = params;
+ nt.in.params.length = 8;
+
+ ndr = ndr_push_init_ctx(NULL, NULL);
+ if (!ndr) return NULL;
+
+ ndr_err = ndr_push_security_descriptor(ndr, NDR_SCALARS|NDR_BUFFERS, io->set_secdesc.in.sd);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(ndr);
+ return NULL;
+ }
+
+ nt.in.data = ndr_push_blob(ndr);
+
+ req = smb_raw_nttrans_send(tree, &nt);
+
+ talloc_free(ndr);
+ return req;
+}
+
+/****************************************************************************
+set file ACL (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_set_secdesc(struct smbcli_tree *tree,
+ union smb_setfileinfo *io)
+{
+ struct smbcli_request *req = smb_raw_set_secdesc_send(tree, io);
+ return smbcli_request_simple_recv(req);
+}
diff --git a/source4/libcli/raw/rawdate.c b/source4/libcli/raw/rawdate.c
new file mode 100644
index 0000000000..5264653cc6
--- /dev/null
+++ b/source4/libcli/raw/rawdate.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ raw date handling functions
+
+ 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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+/*******************************************************************
+put a dos date into a buffer (time/date format)
+This takes GMT time and puts local time for zone_offset in the buffer
+********************************************************************/
+void raw_push_dos_date(struct smbcli_transport *transport,
+ uint8_t *buf, int offset, time_t unixdate)
+{
+ push_dos_date(buf, offset, unixdate, transport->negotiate.server_zone);
+}
+
+/*******************************************************************
+put a dos date into a buffer (date/time format)
+This takes GMT time and puts local time in the buffer
+********************************************************************/
+void raw_push_dos_date2(struct smbcli_transport *transport,
+ uint8_t *buf, int offset, time_t unixdate)
+{
+ push_dos_date2(buf, offset, unixdate, transport->negotiate.server_zone);
+}
+
+/*******************************************************************
+put a dos 32 bit "unix like" date into a buffer. This routine takes
+GMT and converts it to LOCAL time in zone_offset before putting it
+********************************************************************/
+void raw_push_dos_date3(struct smbcli_transport *transport,
+ uint8_t *buf, int offset, time_t unixdate)
+{
+ push_dos_date3(buf, offset, unixdate, transport->negotiate.server_zone);
+}
+
+/*******************************************************************
+convert a dos date
+********************************************************************/
+time_t raw_pull_dos_date(struct smbcli_transport *transport,
+ const uint8_t *date_ptr)
+{
+ return pull_dos_date(date_ptr, transport->negotiate.server_zone);
+}
+
+/*******************************************************************
+like raw_pull_dos_date() but the words are reversed
+********************************************************************/
+time_t raw_pull_dos_date2(struct smbcli_transport *transport,
+ const uint8_t *date_ptr)
+{
+ return pull_dos_date2(date_ptr, transport->negotiate.server_zone);
+}
+
+/*******************************************************************
+ create a unix GMT date from a dos date in 32 bit "unix like" format
+ these arrive in server zone, with corresponding DST
+ ******************************************************************/
+time_t raw_pull_dos_date3(struct smbcli_transport *transport,
+ const uint8_t *date_ptr)
+{
+ return pull_dos_date3(date_ptr, transport->negotiate.server_zone);
+}
diff --git a/source4/libcli/raw/raweas.c b/source4/libcli/raw/raweas.c
new file mode 100644
index 0000000000..09fd4aa412
--- /dev/null
+++ b/source4/libcli/raw/raweas.c
@@ -0,0 +1,365 @@
+/*
+ Unix SMB/CIFS implementation.
+ parsing of EA (extended attribute) lists
+ Copyright (C) Andrew Tridgell 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 "smb.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+/*
+ work out how many bytes on the wire a ea list will consume.
+ This assumes the names are strict ascii, which should be a
+ reasonable assumption
+*/
+size_t ea_list_size(uint_t num_eas, struct ea_struct *eas)
+{
+ uint_t total = 4;
+ int i;
+ for (i=0;i<num_eas;i++) {
+ total += 4 + strlen(eas[i].name.s)+1 + eas[i].value.length;
+ }
+ return total;
+}
+
+/*
+ work out how many bytes on the wire a ea name list will consume.
+*/
+static uint_t ea_name_list_size(uint_t num_names, struct ea_name *eas)
+{
+ uint_t total = 4;
+ int i;
+ for (i=0;i<num_names;i++) {
+ total += 1 + strlen(eas[i].name.s) + 1;
+ }
+ return total;
+}
+
+/*
+ work out how many bytes on the wire a chained ea list will consume.
+ This assumes the names are strict ascii, which should be a
+ reasonable assumption
+*/
+size_t ea_list_size_chained(uint_t num_eas, struct ea_struct *eas, unsigned alignment)
+{
+ uint_t total = 0;
+ int i;
+ for (i=0;i<num_eas;i++) {
+ uint_t len = 8 + strlen(eas[i].name.s)+1 + eas[i].value.length;
+ len = (len + (alignment-1)) & ~(alignment-1);
+ total += len;
+ }
+ return total;
+}
+
+/*
+ put a ea_list into a pre-allocated buffer - buffer must be at least
+ of size ea_list_size()
+*/
+void ea_put_list(uint8_t *data, uint_t num_eas, struct ea_struct *eas)
+{
+ int i;
+ uint32_t ea_size;
+
+ ea_size = ea_list_size(num_eas, eas);
+
+ SIVAL(data, 0, ea_size);
+ data += 4;
+
+ for (i=0;i<num_eas;i++) {
+ uint_t nlen = strlen(eas[i].name.s);
+ SCVAL(data, 0, eas[i].flags);
+ SCVAL(data, 1, nlen);
+ SSVAL(data, 2, eas[i].value.length);
+ memcpy(data+4, eas[i].name.s, nlen+1);
+ memcpy(data+4+nlen+1, eas[i].value.data, eas[i].value.length);
+ data += 4+nlen+1+eas[i].value.length;
+ }
+}
+
+
+/*
+ put a chained ea_list into a pre-allocated buffer - buffer must be
+ at least of size ea_list_size()
+*/
+void ea_put_list_chained(uint8_t *data, uint_t num_eas, struct ea_struct *eas,
+ unsigned alignment)
+{
+ int i;
+
+ for (i=0;i<num_eas;i++) {
+ uint_t nlen = strlen(eas[i].name.s);
+ uint32_t len = 8+nlen+1+eas[i].value.length;
+ uint_t pad = ((len + (alignment-1)) & ~(alignment-1)) - len;
+ if (i == num_eas-1) {
+ SIVAL(data, 0, 0);
+ } else {
+ SIVAL(data, 0, len+pad);
+ }
+ SCVAL(data, 4, eas[i].flags);
+ SCVAL(data, 5, nlen);
+ SSVAL(data, 6, eas[i].value.length);
+ memcpy(data+8, eas[i].name.s, nlen+1);
+ memcpy(data+8+nlen+1, eas[i].value.data, eas[i].value.length);
+ memset(data+len, 0, pad);
+ data += len + pad;
+ }
+}
+
+
+/*
+ pull a ea_struct from a buffer. Return the number of bytes consumed
+*/
+uint_t ea_pull_struct(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ struct ea_struct *ea)
+{
+ uint8_t nlen;
+ uint16_t vlen;
+
+ ZERO_STRUCTP(ea);
+
+ if (blob->length < 6) {
+ return 0;
+ }
+
+ ea->flags = CVAL(blob->data, 0);
+ nlen = CVAL(blob->data, 1);
+ vlen = SVAL(blob->data, 2);
+
+ if (nlen+1+vlen > blob->length-4) {
+ return 0;
+ }
+
+ ea->name.s = talloc_strndup(mem_ctx, (const char *)(blob->data+4), nlen);
+ ea->name.private_length = nlen;
+ ea->value = data_blob_talloc(mem_ctx, NULL, vlen+1);
+ if (!ea->value.data) return 0;
+ if (vlen) {
+ memcpy(ea->value.data, blob->data+4+nlen+1, vlen);
+ }
+ ea->value.data[vlen] = 0;
+ ea->value.length--;
+
+ return 4 + nlen+1 + vlen;
+}
+
+
+/*
+ pull a ea_list from a buffer
+*/
+NTSTATUS ea_pull_list(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ uint_t *num_eas, struct ea_struct **eas)
+{
+ int n;
+ uint32_t ea_size, ofs;
+
+ if (blob->length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ ea_size = IVAL(blob->data, 0);
+ if (ea_size > blob->length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ofs = 4;
+ n = 0;
+ *num_eas = 0;
+ *eas = NULL;
+
+ while (ofs < ea_size) {
+ uint_t len;
+ DATA_BLOB blob2;
+
+ blob2.data = blob->data + ofs;
+ blob2.length = ea_size - ofs;
+
+ *eas = talloc_realloc(mem_ctx, *eas, struct ea_struct, n+1);
+ if (! *eas) return NT_STATUS_NO_MEMORY;
+
+ len = ea_pull_struct(&blob2, mem_ctx, &(*eas)[n]);
+ if (len == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ofs += len;
+ n++;
+ }
+
+ *num_eas = n;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ pull a chained ea_list from a buffer
+*/
+NTSTATUS ea_pull_list_chained(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ uint_t *num_eas, struct ea_struct **eas)
+{
+ int n;
+ uint32_t ofs;
+
+ if (blob->length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ ofs = 0;
+ n = 0;
+ *num_eas = 0;
+ *eas = NULL;
+
+ while (ofs < blob->length) {
+ uint_t len;
+ DATA_BLOB blob2;
+ uint32_t next_ofs = IVAL(blob->data, ofs);
+
+ blob2.data = blob->data + ofs + 4;
+ blob2.length = blob->length - (ofs + 4);
+
+ *eas = talloc_realloc(mem_ctx, *eas, struct ea_struct, n+1);
+ if (! *eas) return NT_STATUS_NO_MEMORY;
+
+ len = ea_pull_struct(&blob2, mem_ctx, &(*eas)[n]);
+ if (len == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ofs += next_ofs;
+
+ if (ofs+4 > blob->length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ n++;
+ if (next_ofs == 0) break;
+ }
+
+ *num_eas = n;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ pull a ea_name from a buffer. Return the number of bytes consumed
+*/
+static uint_t ea_pull_name(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ struct ea_name *ea)
+{
+ uint8_t nlen;
+
+ if (blob->length < 2) {
+ return 0;
+ }
+
+ nlen = CVAL(blob->data, 0);
+
+ if (nlen+2 > blob->length) {
+ return 0;
+ }
+
+ ea->name.s = talloc_strndup(mem_ctx, (const char *)(blob->data+1), nlen);
+ ea->name.private_length = nlen;
+
+ return nlen+2;
+}
+
+
+/*
+ pull a ea_name list from a buffer
+*/
+NTSTATUS ea_pull_name_list(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ uint_t *num_names, struct ea_name **ea_names)
+{
+ int n;
+ uint32_t ea_size, ofs;
+
+ if (blob->length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ ea_size = IVAL(blob->data, 0);
+ if (ea_size > blob->length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ofs = 4;
+ n = 0;
+ *num_names = 0;
+ *ea_names = NULL;
+
+ while (ofs < ea_size) {
+ uint_t len;
+ DATA_BLOB blob2;
+
+ blob2.data = blob->data + ofs;
+ blob2.length = ea_size - ofs;
+
+ *ea_names = talloc_realloc(mem_ctx, *ea_names, struct ea_name, n+1);
+ if (! *ea_names) return NT_STATUS_NO_MEMORY;
+
+ len = ea_pull_name(&blob2, mem_ctx, &(*ea_names)[n]);
+ if (len == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ofs += len;
+ n++;
+ }
+
+ *num_names = n;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ put a ea_name list into a data blob
+*/
+bool ea_push_name_list(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *data, uint_t num_names, struct ea_name *eas)
+{
+ int i;
+ uint32_t ea_size;
+ uint32_t off;
+
+ ea_size = ea_name_list_size(num_names, eas);
+
+ *data = data_blob_talloc(mem_ctx, NULL, ea_size);
+ if (data->data == NULL) {
+ return false;
+ }
+
+ SIVAL(data->data, 0, ea_size);
+ off = 4;
+
+ for (i=0;i<num_names;i++) {
+ uint_t nlen = strlen(eas[i].name.s);
+ SCVAL(data->data, off, nlen);
+ memcpy(data->data+off+1, eas[i].name.s, nlen+1);
+ off += 1+nlen+1;
+ }
+
+ return true;
+}
diff --git a/source4/libcli/raw/rawfile.c b/source4/libcli/raw/rawfile.c
new file mode 100644
index 0000000000..63de051b33
--- /dev/null
+++ b/source4/libcli/raw/rawfile.c
@@ -0,0 +1,1017 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file operations
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2001-2002
+ Copyright (C) James Myers 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 "smb.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+#define SETUP_REQUEST(cmd, wct, buflen) do { \
+ req = smbcli_request_setup(tree, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+/**
+ Return a string representing a CIFS attribute for a file.
+**/
+char *attrib_string(TALLOC_CTX *mem_ctx, uint32_t attrib)
+{
+ int i, len;
+ const struct {
+ char c;
+ uint16_t attr;
+ } attr_strs[] = {
+ {'V', FILE_ATTRIBUTE_VOLUME},
+ {'D', FILE_ATTRIBUTE_DIRECTORY},
+ {'A', FILE_ATTRIBUTE_ARCHIVE},
+ {'H', FILE_ATTRIBUTE_HIDDEN},
+ {'S', FILE_ATTRIBUTE_SYSTEM},
+ {'N', FILE_ATTRIBUTE_NORMAL},
+ {'R', FILE_ATTRIBUTE_READONLY},
+ {'d', FILE_ATTRIBUTE_DEVICE},
+ {'t', FILE_ATTRIBUTE_TEMPORARY},
+ {'s', FILE_ATTRIBUTE_SPARSE},
+ {'r', FILE_ATTRIBUTE_REPARSE_POINT},
+ {'c', FILE_ATTRIBUTE_COMPRESSED},
+ {'o', FILE_ATTRIBUTE_OFFLINE},
+ {'n', FILE_ATTRIBUTE_NONINDEXED},
+ {'e', FILE_ATTRIBUTE_ENCRYPTED}
+ };
+ char *ret;
+
+ ret = talloc_array(mem_ctx, char, ARRAY_SIZE(attr_strs)+1);
+ if (!ret) {
+ return NULL;
+ }
+
+ for (len=i=0; i<ARRAY_SIZE(attr_strs); i++) {
+ if (attrib & attr_strs[i].attr) {
+ ret[len++] = attr_strs[i].c;
+ }
+ }
+
+ ret[len] = 0;
+
+ talloc_set_name_const(ret, ret);
+
+ return ret;
+}
+
+/****************************************************************************
+ Rename a file - async interface
+****************************************************************************/
+struct smbcli_request *smb_raw_rename_send(struct smbcli_tree *tree,
+ union smb_rename *parms)
+{
+ struct smbcli_request *req = NULL;
+ struct smb_nttrans nt;
+ TALLOC_CTX *mem_ctx;
+
+ switch (parms->generic.level) {
+ case RAW_RENAME_RENAME:
+ SETUP_REQUEST(SMBmv, 1, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->rename.in.attrib);
+ smbcli_req_append_ascii4(req, parms->rename.in.pattern1, STR_TERMINATE);
+ smbcli_req_append_ascii4(req, parms->rename.in.pattern2, STR_TERMINATE);
+ break;
+
+ case RAW_RENAME_NTRENAME:
+ SETUP_REQUEST(SMBntrename, 4, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->ntrename.in.attrib);
+ SSVAL(req->out.vwv, VWV(1), parms->ntrename.in.flags);
+ SIVAL(req->out.vwv, VWV(2), parms->ntrename.in.cluster_size);
+ smbcli_req_append_ascii4(req, parms->ntrename.in.old_name, STR_TERMINATE);
+ smbcli_req_append_ascii4(req, parms->ntrename.in.new_name, STR_TERMINATE);
+ break;
+
+ case RAW_RENAME_NTTRANS:
+
+ mem_ctx = talloc_new(tree);
+
+ nt.in.max_setup = 0;
+ nt.in.max_param = 0;
+ nt.in.max_data = 0;
+ nt.in.setup_count = 0;
+ nt.in.setup = NULL;
+ nt.in.function = NT_TRANSACT_RENAME;
+ nt.in.params = data_blob_talloc(mem_ctx, NULL, 4);
+ nt.in.data = data_blob(NULL, 0);
+
+ SSVAL(nt.in.params.data, VWV(0), parms->nttrans.in.file.fnum);
+ SSVAL(nt.in.params.data, VWV(1), parms->nttrans.in.flags);
+
+ smbcli_blob_append_string(tree->session, mem_ctx,
+ &nt.in.params, parms->nttrans.in.new_name,
+ STR_TERMINATE);
+
+ req = smb_raw_nttrans_send(tree, &nt);
+ talloc_free(mem_ctx);
+ return req;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Rename a file - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_rename(struct smbcli_tree *tree,
+ union smb_rename *parms)
+{
+ struct smbcli_request *req = smb_raw_rename_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Delete a file - async interface
+****************************************************************************/
+struct smbcli_request *smb_raw_unlink_send(struct smbcli_tree *tree,
+ union smb_unlink *parms)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST(SMBunlink, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), parms->unlink.in.attrib);
+ smbcli_req_append_ascii4(req, parms->unlink.in.pattern, STR_TERMINATE);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+ return req;
+}
+
+/*
+ delete a file - sync interface
+*/
+_PUBLIC_ NTSTATUS smb_raw_unlink(struct smbcli_tree *tree,
+ union smb_unlink *parms)
+{
+ struct smbcli_request *req = smb_raw_unlink_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ create a directory using TRANSACT2_MKDIR - async interface
+****************************************************************************/
+static struct smbcli_request *smb_raw_t2mkdir_send(struct smbcli_tree *tree,
+ union smb_mkdir *parms)
+{
+ struct smb_trans2 t2;
+ uint16_t setup = TRANSACT2_MKDIR;
+ TALLOC_CTX *mem_ctx;
+ struct smbcli_request *req;
+ uint16_t data_total;
+
+ mem_ctx = talloc_init("t2mkdir");
+
+ data_total = ea_list_size(parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas);
+
+ t2.in.max_param = 2;
+ t2.in.max_data = 0;
+ t2.in.max_setup = 0;
+ t2.in.flags = 0;
+ t2.in.timeout = 0;
+ t2.in.setup_count = 1;
+ t2.in.setup = &setup;
+ t2.in.params = data_blob_talloc(mem_ctx, NULL, 4);
+ t2.in.data = data_blob_talloc(mem_ctx, NULL, data_total);
+
+ SIVAL(t2.in.params.data, VWV(0), 0); /* reserved */
+
+ smbcli_blob_append_string(tree->session, mem_ctx,
+ &t2.in.params, parms->t2mkdir.in.path, STR_TERMINATE);
+
+ ea_put_list(t2.in.data.data, parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas);
+
+ req = smb_raw_trans2_send(tree, &t2);
+
+ talloc_free(mem_ctx);
+
+ return req;
+}
+
+/****************************************************************************
+ Create a directory - async interface
+****************************************************************************/
+struct smbcli_request *smb_raw_mkdir_send(struct smbcli_tree *tree,
+ union smb_mkdir *parms)
+{
+ struct smbcli_request *req;
+
+ if (parms->generic.level == RAW_MKDIR_T2MKDIR) {
+ return smb_raw_t2mkdir_send(tree, parms);
+ }
+
+ if (parms->generic.level != RAW_MKDIR_MKDIR) {
+ return NULL;
+ }
+
+ SETUP_REQUEST(SMBmkdir, 0, 0);
+
+ smbcli_req_append_ascii4(req, parms->mkdir.in.path, STR_TERMINATE);
+
+ if (!smbcli_request_send(req)) {
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Create a directory - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_mkdir(struct smbcli_tree *tree,
+ union smb_mkdir *parms)
+{
+ struct smbcli_request *req = smb_raw_mkdir_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+/****************************************************************************
+ Remove a directory - async interface
+****************************************************************************/
+struct smbcli_request *smb_raw_rmdir_send(struct smbcli_tree *tree,
+ struct smb_rmdir *parms)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST(SMBrmdir, 0, 0);
+
+ smbcli_req_append_ascii4(req, parms->in.path, STR_TERMINATE);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Remove a directory - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_rmdir(struct smbcli_tree *tree,
+ struct smb_rmdir *parms)
+{
+ struct smbcli_request *req = smb_raw_rmdir_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/*
+ Open a file using TRANSACT2_OPEN - async recv
+*/
+static NTSTATUS smb_raw_nttrans_create_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_open *parms)
+{
+ NTSTATUS status;
+ struct smb_nttrans nt;
+ uint8_t *params;
+
+ status = smb_raw_nttrans_recv(req, mem_ctx, &nt);
+ if (!NT_STATUS_IS_OK(status)) return status;
+
+ if (nt.out.params.length < 69) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ params = nt.out.params.data;
+
+ parms->ntcreatex.out.oplock_level = CVAL(params, 0);
+ parms->ntcreatex.out.file.fnum = SVAL(params, 2);
+ parms->ntcreatex.out.create_action = IVAL(params, 4);
+ parms->ntcreatex.out.create_time = smbcli_pull_nttime(params, 12);
+ parms->ntcreatex.out.access_time = smbcli_pull_nttime(params, 20);
+ parms->ntcreatex.out.write_time = smbcli_pull_nttime(params, 28);
+ parms->ntcreatex.out.change_time = smbcli_pull_nttime(params, 36);
+ parms->ntcreatex.out.attrib = IVAL(params, 44);
+ parms->ntcreatex.out.alloc_size = BVAL(params, 48);
+ parms->ntcreatex.out.size = BVAL(params, 56);
+ parms->ntcreatex.out.file_type = SVAL(params, 64);
+ parms->ntcreatex.out.ipc_state = SVAL(params, 66);
+ parms->ntcreatex.out.is_directory = CVAL(params, 68);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ Open a file using NTTRANS CREATE - async send
+*/
+static struct smbcli_request *smb_raw_nttrans_create_send(struct smbcli_tree *tree,
+ union smb_open *parms)
+{
+ struct smb_nttrans nt;
+ uint8_t *params;
+ TALLOC_CTX *mem_ctx = talloc_new(tree);
+ uint16_t fname_len;
+ DATA_BLOB sd_blob, ea_blob;
+ struct smbcli_request *req;
+
+ nt.in.max_setup = 0;
+ nt.in.max_param = 101;
+ nt.in.max_data = 0;
+ nt.in.setup_count = 0;
+ nt.in.function = NT_TRANSACT_CREATE;
+ nt.in.setup = NULL;
+
+ sd_blob = data_blob(NULL, 0);
+ ea_blob = data_blob(NULL, 0);
+
+ if (parms->ntcreatex.in.sec_desc) {
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_push_struct_blob(&sd_blob, mem_ctx, NULL,
+ parms->ntcreatex.in.sec_desc,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ }
+
+ if (parms->ntcreatex.in.ea_list) {
+ uint32_t ea_size = ea_list_size_chained(parms->ntcreatex.in.ea_list->num_eas,
+ parms->ntcreatex.in.ea_list->eas, 4);
+ ea_blob = data_blob_talloc(mem_ctx, NULL, ea_size);
+ if (ea_blob.data == NULL) {
+ return NULL;
+ }
+ ea_put_list_chained(ea_blob.data,
+ parms->ntcreatex.in.ea_list->num_eas,
+ parms->ntcreatex.in.ea_list->eas, 4);
+ }
+
+ nt.in.params = data_blob_talloc(mem_ctx, NULL, 53);
+ if (nt.in.params.data == NULL) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ /* build the parameter section */
+ params = nt.in.params.data;
+
+ SIVAL(params, 0, parms->ntcreatex.in.flags);
+ SIVAL(params, 4, parms->ntcreatex.in.root_fid);
+ SIVAL(params, 8, parms->ntcreatex.in.access_mask);
+ SBVAL(params, 12, parms->ntcreatex.in.alloc_size);
+ SIVAL(params, 20, parms->ntcreatex.in.file_attr);
+ SIVAL(params, 24, parms->ntcreatex.in.share_access);
+ SIVAL(params, 28, parms->ntcreatex.in.open_disposition);
+ SIVAL(params, 32, parms->ntcreatex.in.create_options);
+ SIVAL(params, 36, sd_blob.length);
+ SIVAL(params, 40, ea_blob.length);
+ SIVAL(params, 48, parms->ntcreatex.in.impersonation);
+ SCVAL(params, 52, parms->ntcreatex.in.security_flags);
+
+ /* the empty string first forces the correct alignment */
+ smbcli_blob_append_string(tree->session, mem_ctx, &nt.in.params,"", 0);
+ fname_len = smbcli_blob_append_string(tree->session, mem_ctx, &nt.in.params,
+ parms->ntcreatex.in.fname, STR_TERMINATE);
+
+ SIVAL(nt.in.params.data, 44, fname_len);
+
+ /* build the data section */
+ nt.in.data = data_blob_talloc(mem_ctx, NULL, sd_blob.length + ea_blob.length);
+ memcpy(nt.in.data.data, sd_blob.data, sd_blob.length);
+ memcpy(nt.in.data.data+sd_blob.length, ea_blob.data, ea_blob.length);
+
+ /* send the request on its way */
+ req = smb_raw_nttrans_send(tree, &nt);
+
+ talloc_free(mem_ctx);
+
+ return req;
+}
+
+
+/****************************************************************************
+ Open a file using TRANSACT2_OPEN - async send
+****************************************************************************/
+static struct smbcli_request *smb_raw_t2open_send(struct smbcli_tree *tree,
+ union smb_open *parms)
+{
+ struct smb_trans2 t2;
+ uint16_t setup = TRANSACT2_OPEN;
+ TALLOC_CTX *mem_ctx = talloc_init("smb_raw_t2open");
+ struct smbcli_request *req;
+ uint16_t list_size;
+
+ list_size = ea_list_size(parms->t2open.in.num_eas, parms->t2open.in.eas);
+
+ t2.in.max_param = 30;
+ t2.in.max_data = 0;
+ t2.in.max_setup = 0;
+ t2.in.flags = 0;
+ t2.in.timeout = 0;
+ t2.in.setup_count = 1;
+ t2.in.setup = &setup;
+ t2.in.params = data_blob_talloc(mem_ctx, NULL, 28);
+ t2.in.data = data_blob_talloc(mem_ctx, NULL, list_size);
+
+ SSVAL(t2.in.params.data, VWV(0), parms->t2open.in.flags);
+ SSVAL(t2.in.params.data, VWV(1), parms->t2open.in.open_mode);
+ SSVAL(t2.in.params.data, VWV(2), parms->t2open.in.search_attrs);
+ SSVAL(t2.in.params.data, VWV(3), parms->t2open.in.file_attrs);
+ raw_push_dos_date(tree->session->transport,
+ t2.in.params.data, VWV(4), parms->t2open.in.write_time);
+ SSVAL(t2.in.params.data, VWV(6), parms->t2open.in.open_func);
+ SIVAL(t2.in.params.data, VWV(7), parms->t2open.in.size);
+ SIVAL(t2.in.params.data, VWV(9), parms->t2open.in.timeout);
+ SIVAL(t2.in.params.data, VWV(11), 0);
+ SSVAL(t2.in.params.data, VWV(13), 0);
+
+ smbcli_blob_append_string(tree->session, mem_ctx,
+ &t2.in.params, parms->t2open.in.fname,
+ STR_TERMINATE);
+
+ ea_put_list(t2.in.data.data, parms->t2open.in.num_eas, parms->t2open.in.eas);
+
+ req = smb_raw_trans2_send(tree, &t2);
+
+ talloc_free(mem_ctx);
+
+ return req;
+}
+
+
+/****************************************************************************
+ Open a file using TRANSACT2_OPEN - async recv
+****************************************************************************/
+static NTSTATUS smb_raw_t2open_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms)
+{
+ struct smbcli_transport *transport = req->transport;
+ struct smb_trans2 t2;
+ NTSTATUS status;
+
+ status = smb_raw_trans2_recv(req, mem_ctx, &t2);
+ if (!NT_STATUS_IS_OK(status)) return status;
+
+ if (t2.out.params.length < 30) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ parms->t2open.out.file.fnum = SVAL(t2.out.params.data, VWV(0));
+ parms->t2open.out.attrib = SVAL(t2.out.params.data, VWV(1));
+ parms->t2open.out.write_time = raw_pull_dos_date3(transport, t2.out.params.data + VWV(2));
+ parms->t2open.out.size = IVAL(t2.out.params.data, VWV(4));
+ parms->t2open.out.access = SVAL(t2.out.params.data, VWV(6));
+ parms->t2open.out.ftype = SVAL(t2.out.params.data, VWV(7));
+ parms->t2open.out.devstate = SVAL(t2.out.params.data, VWV(8));
+ parms->t2open.out.action = SVAL(t2.out.params.data, VWV(9));
+ parms->t2open.out.file_id = SVAL(t2.out.params.data, VWV(10));
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Open a file - async send
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_open_send(struct smbcli_tree *tree, union smb_open *parms)
+{
+ int len;
+ struct smbcli_request *req = NULL;
+ bool bigoffset = false;
+
+ switch (parms->generic.level) {
+ case RAW_OPEN_T2OPEN:
+ return smb_raw_t2open_send(tree, parms);
+
+ case RAW_OPEN_OPEN:
+ SETUP_REQUEST(SMBopen, 2, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->openold.in.open_mode);
+ SSVAL(req->out.vwv, VWV(1), parms->openold.in.search_attrs);
+ smbcli_req_append_ascii4(req, parms->openold.in.fname, STR_TERMINATE);
+ break;
+
+ case RAW_OPEN_OPENX:
+ SETUP_REQUEST(SMBopenX, 15, 0);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->openx.in.flags);
+ SSVAL(req->out.vwv, VWV(3), parms->openx.in.open_mode);
+ SSVAL(req->out.vwv, VWV(4), parms->openx.in.search_attrs);
+ SSVAL(req->out.vwv, VWV(5), parms->openx.in.file_attrs);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(6), parms->openx.in.write_time);
+ SSVAL(req->out.vwv, VWV(8), parms->openx.in.open_func);
+ SIVAL(req->out.vwv, VWV(9), parms->openx.in.size);
+ SIVAL(req->out.vwv, VWV(11),parms->openx.in.timeout);
+ SIVAL(req->out.vwv, VWV(13),0); /* reserved */
+ smbcli_req_append_string(req, parms->openx.in.fname, STR_TERMINATE);
+ break;
+
+ case RAW_OPEN_MKNEW:
+ SETUP_REQUEST(SMBmknew, 3, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->mknew.in.attrib);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(1), parms->mknew.in.write_time);
+ smbcli_req_append_ascii4(req, parms->mknew.in.fname, STR_TERMINATE);
+ break;
+
+ case RAW_OPEN_CREATE:
+ SETUP_REQUEST(SMBcreate, 3, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->create.in.attrib);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(1), parms->create.in.write_time);
+ smbcli_req_append_ascii4(req, parms->create.in.fname, STR_TERMINATE);
+ break;
+
+ case RAW_OPEN_CTEMP:
+ SETUP_REQUEST(SMBctemp, 3, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->ctemp.in.attrib);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(1), parms->ctemp.in.write_time);
+ smbcli_req_append_ascii4(req, parms->ctemp.in.directory, STR_TERMINATE);
+ break;
+
+ case RAW_OPEN_SPLOPEN:
+ SETUP_REQUEST(SMBsplopen, 2, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->splopen.in.setup_length);
+ SSVAL(req->out.vwv, VWV(1), parms->splopen.in.mode);
+ break;
+
+ case RAW_OPEN_NTCREATEX:
+ SETUP_REQUEST(SMBntcreateX, 24, 0);
+ SSVAL(req->out.vwv, VWV(0),SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1),0);
+ SCVAL(req->out.vwv, VWV(2),0); /* padding */
+ SIVAL(req->out.vwv, 7, parms->ntcreatex.in.flags);
+ SIVAL(req->out.vwv, 11, parms->ntcreatex.in.root_fid);
+ SIVAL(req->out.vwv, 15, parms->ntcreatex.in.access_mask);
+ SBVAL(req->out.vwv, 19, parms->ntcreatex.in.alloc_size);
+ SIVAL(req->out.vwv, 27, parms->ntcreatex.in.file_attr);
+ SIVAL(req->out.vwv, 31, parms->ntcreatex.in.share_access);
+ SIVAL(req->out.vwv, 35, parms->ntcreatex.in.open_disposition);
+ SIVAL(req->out.vwv, 39, parms->ntcreatex.in.create_options);
+ SIVAL(req->out.vwv, 43, parms->ntcreatex.in.impersonation);
+ SCVAL(req->out.vwv, 47, parms->ntcreatex.in.security_flags);
+
+ smbcli_req_append_string_len(req, parms->ntcreatex.in.fname, STR_TERMINATE, &len);
+ SSVAL(req->out.vwv, 5, len);
+ break;
+
+ case RAW_OPEN_NTTRANS_CREATE:
+ return smb_raw_nttrans_create_send(tree, parms);
+
+
+ case RAW_OPEN_OPENX_READX:
+ SETUP_REQUEST(SMBopenX, 15, 0);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->openxreadx.in.flags);
+ SSVAL(req->out.vwv, VWV(3), parms->openxreadx.in.open_mode);
+ SSVAL(req->out.vwv, VWV(4), parms->openxreadx.in.search_attrs);
+ SSVAL(req->out.vwv, VWV(5), parms->openxreadx.in.file_attrs);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(6), parms->openxreadx.in.write_time);
+ SSVAL(req->out.vwv, VWV(8), parms->openxreadx.in.open_func);
+ SIVAL(req->out.vwv, VWV(9), parms->openxreadx.in.size);
+ SIVAL(req->out.vwv, VWV(11),parms->openxreadx.in.timeout);
+ SIVAL(req->out.vwv, VWV(13),0);
+ smbcli_req_append_string(req, parms->openxreadx.in.fname, STR_TERMINATE);
+
+ if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ bigoffset = true;
+ }
+
+ smbcli_chained_request_setup(req, SMBreadX, bigoffset ? 12 : 10, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), 0);
+ SIVAL(req->out.vwv, VWV(3), parms->openxreadx.in.offset);
+ SSVAL(req->out.vwv, VWV(5), parms->openxreadx.in.maxcnt & 0xFFFF);
+ SSVAL(req->out.vwv, VWV(6), parms->openxreadx.in.mincnt);
+ SIVAL(req->out.vwv, VWV(7), parms->openxreadx.in.maxcnt >> 16);
+ SSVAL(req->out.vwv, VWV(9), parms->openxreadx.in.remaining);
+ if (bigoffset) {
+ SIVAL(req->out.vwv, VWV(10),parms->openxreadx.in.offset>>32);
+ }
+ break;
+ case RAW_OPEN_SMB2:
+ return NULL;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Open a file - async recv
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_open_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms)
+{
+ NTSTATUS status;
+
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ switch (parms->openold.level) {
+ case RAW_OPEN_T2OPEN:
+ return smb_raw_t2open_recv(req, mem_ctx, parms);
+
+ case RAW_OPEN_OPEN:
+ SMBCLI_CHECK_WCT(req, 7);
+ parms->openold.out.file.fnum = SVAL(req->in.vwv, VWV(0));
+ parms->openold.out.attrib = SVAL(req->in.vwv, VWV(1));
+ parms->openold.out.write_time = raw_pull_dos_date3(req->transport,
+ req->in.vwv + VWV(2));
+ parms->openold.out.size = IVAL(req->in.vwv, VWV(4));
+ parms->openold.out.rmode = SVAL(req->in.vwv, VWV(6));
+ break;
+
+ case RAW_OPEN_OPENX:
+ SMBCLI_CHECK_MIN_WCT(req, 15);
+ parms->openx.out.file.fnum = SVAL(req->in.vwv, VWV(2));
+ parms->openx.out.attrib = SVAL(req->in.vwv, VWV(3));
+ parms->openx.out.write_time = raw_pull_dos_date3(req->transport,
+ req->in.vwv + VWV(4));
+ parms->openx.out.size = IVAL(req->in.vwv, VWV(6));
+ parms->openx.out.access = SVAL(req->in.vwv, VWV(8));
+ parms->openx.out.ftype = SVAL(req->in.vwv, VWV(9));
+ parms->openx.out.devstate = SVAL(req->in.vwv, VWV(10));
+ parms->openx.out.action = SVAL(req->in.vwv, VWV(11));
+ parms->openx.out.unique_fid = IVAL(req->in.vwv, VWV(12));
+ if (req->in.wct >= 19) {
+ parms->openx.out.access_mask = IVAL(req->in.vwv, VWV(15));
+ parms->openx.out.unknown = IVAL(req->in.vwv, VWV(17));
+ } else {
+ parms->openx.out.access_mask = 0;
+ parms->openx.out.unknown = 0;
+ }
+ break;
+
+ case RAW_OPEN_MKNEW:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->mknew.out.file.fnum = SVAL(req->in.vwv, VWV(0));
+ break;
+
+ case RAW_OPEN_CREATE:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->create.out.file.fnum = SVAL(req->in.vwv, VWV(0));
+ break;
+
+ case RAW_OPEN_CTEMP:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->ctemp.out.file.fnum = SVAL(req->in.vwv, VWV(0));
+ smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->ctemp.out.name, req->in.data, -1, STR_TERMINATE | STR_ASCII);
+ break;
+
+ case RAW_OPEN_SPLOPEN:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->splopen.out.file.fnum = SVAL(req->in.vwv, VWV(0));
+ break;
+
+ case RAW_OPEN_NTCREATEX:
+ SMBCLI_CHECK_MIN_WCT(req, 34);
+ parms->ntcreatex.out.oplock_level = CVAL(req->in.vwv, 4);
+ parms->ntcreatex.out.file.fnum = SVAL(req->in.vwv, 5);
+ parms->ntcreatex.out.create_action = IVAL(req->in.vwv, 7);
+ parms->ntcreatex.out.create_time = smbcli_pull_nttime(req->in.vwv, 11);
+ parms->ntcreatex.out.access_time = smbcli_pull_nttime(req->in.vwv, 19);
+ parms->ntcreatex.out.write_time = smbcli_pull_nttime(req->in.vwv, 27);
+ parms->ntcreatex.out.change_time = smbcli_pull_nttime(req->in.vwv, 35);
+ parms->ntcreatex.out.attrib = IVAL(req->in.vwv, 43);
+ parms->ntcreatex.out.alloc_size = BVAL(req->in.vwv, 47);
+ parms->ntcreatex.out.size = BVAL(req->in.vwv, 55);
+ parms->ntcreatex.out.file_type = SVAL(req->in.vwv, 63);
+ parms->ntcreatex.out.ipc_state = SVAL(req->in.vwv, 65);
+ parms->ntcreatex.out.is_directory = CVAL(req->in.vwv, 67);
+ break;
+
+ case RAW_OPEN_NTTRANS_CREATE:
+ return smb_raw_nttrans_create_recv(req, mem_ctx, parms);
+
+ case RAW_OPEN_OPENX_READX:
+ SMBCLI_CHECK_MIN_WCT(req, 15);
+ parms->openxreadx.out.file.fnum = SVAL(req->in.vwv, VWV(2));
+ parms->openxreadx.out.attrib = SVAL(req->in.vwv, VWV(3));
+ parms->openxreadx.out.write_time = raw_pull_dos_date3(req->transport,
+ req->in.vwv + VWV(4));
+ parms->openxreadx.out.size = IVAL(req->in.vwv, VWV(6));
+ parms->openxreadx.out.access = SVAL(req->in.vwv, VWV(8));
+ parms->openxreadx.out.ftype = SVAL(req->in.vwv, VWV(9));
+ parms->openxreadx.out.devstate = SVAL(req->in.vwv, VWV(10));
+ parms->openxreadx.out.action = SVAL(req->in.vwv, VWV(11));
+ parms->openxreadx.out.unique_fid = IVAL(req->in.vwv, VWV(12));
+ if (req->in.wct >= 19) {
+ parms->openxreadx.out.access_mask = IVAL(req->in.vwv, VWV(15));
+ parms->openxreadx.out.unknown = IVAL(req->in.vwv, VWV(17));
+ } else {
+ parms->openxreadx.out.access_mask = 0;
+ parms->openxreadx.out.unknown = 0;
+ }
+
+ status = smbcli_chained_advance(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ SMBCLI_CHECK_WCT(req, 12);
+ parms->openxreadx.out.remaining = SVAL(req->in.vwv, VWV(2));
+ parms->openxreadx.out.compaction_mode = SVAL(req->in.vwv, VWV(3));
+ parms->openxreadx.out.nread = SVAL(req->in.vwv, VWV(5));
+ if (parms->openxreadx.out.nread >
+ MAX(parms->openxreadx.in.mincnt, parms->openxreadx.in.maxcnt) ||
+ !smbcli_raw_pull_data(&req->in.bufinfo, req->in.hdr + SVAL(req->in.vwv, VWV(6)),
+ parms->openxreadx.out.nread,
+ parms->openxreadx.out.data)) {
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ case RAW_OPEN_SMB2:
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Open a file - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_open(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_open *parms)
+{
+ struct smbcli_request *req = smb_raw_open_send(tree, parms);
+ return smb_raw_open_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ Close a file - async send
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_close_send(struct smbcli_tree *tree, union smb_close *parms)
+{
+ struct smbcli_request *req = NULL;
+
+ switch (parms->generic.level) {
+ case RAW_CLOSE_CLOSE:
+ SETUP_REQUEST(SMBclose, 3, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->close.in.file.fnum);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(1), parms->close.in.write_time);
+ break;
+
+ case RAW_CLOSE_SPLCLOSE:
+ SETUP_REQUEST(SMBsplclose, 3, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->splclose.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(1), 0); /* reserved */
+ break;
+
+ case RAW_CLOSE_SMB2:
+ return NULL;
+ }
+
+ if (!req) return NULL;
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ Close a file - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_close(struct smbcli_tree *tree, union smb_close *parms)
+{
+ struct smbcli_request *req = smb_raw_close_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Locking calls - async interface
+****************************************************************************/
+struct smbcli_request *smb_raw_lock_send(struct smbcli_tree *tree, union smb_lock *parms)
+{
+ struct smbcli_request *req = NULL;
+
+ switch (parms->generic.level) {
+ case RAW_LOCK_LOCK:
+ SETUP_REQUEST(SMBlock, 5, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->lock.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(1), parms->lock.in.count);
+ SIVAL(req->out.vwv, VWV(3), parms->lock.in.offset);
+ break;
+
+ case RAW_LOCK_UNLOCK:
+ SETUP_REQUEST(SMBunlock, 5, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->unlock.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(1), parms->unlock.in.count);
+ SIVAL(req->out.vwv, VWV(3), parms->unlock.in.offset);
+ break;
+
+ case RAW_LOCK_LOCKX: {
+ struct smb_lock_entry *lockp;
+ uint_t lck_size = (parms->lockx.in.mode & LOCKING_ANDX_LARGE_FILES)? 20 : 10;
+ uint_t lock_count = parms->lockx.in.ulock_cnt + parms->lockx.in.lock_cnt;
+ int i;
+
+ SETUP_REQUEST(SMBlockingX, 8, lck_size * lock_count);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->lockx.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(3), parms->lockx.in.mode);
+ SIVAL(req->out.vwv, VWV(4), parms->lockx.in.timeout);
+ SSVAL(req->out.vwv, VWV(6), parms->lockx.in.ulock_cnt);
+ SSVAL(req->out.vwv, VWV(7), parms->lockx.in.lock_cnt);
+
+ /* copy in all the locks */
+ lockp = &parms->lockx.in.locks[0];
+ for (i = 0; i < lock_count; i++) {
+ uint8_t *p = req->out.data + lck_size * i;
+ SSVAL(p, 0, lockp[i].pid);
+ if (parms->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) {
+ SSVAL(p, 2, 0); /* reserved */
+ SIVAL(p, 4, lockp[i].offset>>32);
+ SIVAL(p, 8, lockp[i].offset);
+ SIVAL(p, 12, lockp[i].count>>32);
+ SIVAL(p, 16, lockp[i].count);
+ } else {
+ SIVAL(p, 2, lockp[i].offset);
+ SIVAL(p, 6, lockp[i].count);
+ }
+ }
+ break;
+ }
+ case RAW_LOCK_SMB2:
+ return NULL;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Locking calls - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_lock(struct smbcli_tree *tree, union smb_lock *parms)
+{
+ struct smbcli_request *req = smb_raw_lock_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Check for existence of a dir - async send
+****************************************************************************/
+struct smbcli_request *smb_raw_chkpath_send(struct smbcli_tree *tree, union smb_chkpath *parms)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST(SMBchkpth, 0, 0);
+
+ smbcli_req_append_ascii4(req, parms->chkpath.in.path, STR_TERMINATE);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Check for existence of a dir - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_chkpath(struct smbcli_tree *tree, union smb_chkpath *parms)
+{
+ struct smbcli_request *req = smb_raw_chkpath_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+/****************************************************************************
+ flush a file - async send
+ a flush with RAW_FLUSH_ALL will flush all files
+****************************************************************************/
+struct smbcli_request *smb_raw_flush_send(struct smbcli_tree *tree, union smb_flush *parms)
+{
+ struct smbcli_request *req;
+ uint16_t fnum=0;
+
+ switch (parms->generic.level) {
+ case RAW_FLUSH_FLUSH:
+ fnum = parms->flush.in.file.fnum;
+ break;
+ case RAW_FLUSH_ALL:
+ fnum = 0xFFFF;
+ break;
+ case RAW_FLUSH_SMB2:
+ return NULL;
+ }
+
+ SETUP_REQUEST(SMBflush, 1, 0);
+ SSVAL(req->out.vwv, VWV(0), fnum);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ flush a file - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_flush(struct smbcli_tree *tree, union smb_flush *parms)
+{
+ struct smbcli_request *req = smb_raw_flush_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ seek a file - async send
+****************************************************************************/
+struct smbcli_request *smb_raw_seek_send(struct smbcli_tree *tree,
+ union smb_seek *parms)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST(SMBlseek, 4, 0);
+
+ SSVAL(req->out.vwv, VWV(0), parms->lseek.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->lseek.in.mode);
+ SIVALS(req->out.vwv, VWV(2), parms->lseek.in.offset);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+ return req;
+}
+
+/****************************************************************************
+ seek a file - async receive
+****************************************************************************/
+NTSTATUS smb_raw_seek_recv(struct smbcli_request *req,
+ union smb_seek *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ SMBCLI_CHECK_WCT(req, 2);
+ parms->lseek.out.offset = IVAL(req->in.vwv, VWV(0));
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+/*
+ seek a file - sync interface
+*/
+_PUBLIC_ NTSTATUS smb_raw_seek(struct smbcli_tree *tree,
+ union smb_seek *parms)
+{
+ struct smbcli_request *req = smb_raw_seek_send(tree, parms);
+ return smb_raw_seek_recv(req, parms);
+}
diff --git a/source4/libcli/raw/rawfileinfo.c b/source4/libcli/raw/rawfileinfo.c
new file mode 100644
index 0000000000..09ecb40002
--- /dev/null
+++ b/source4/libcli/raw/rawfileinfo.c
@@ -0,0 +1,776 @@
+/*
+ Unix SMB/CIFS implementation.
+ client trans2 operations
+ Copyright (C) James Myers 2003
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James Peach 2007
+
+ 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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+/* local macros to make the code more readable */
+#define FINFO_CHECK_MIN_SIZE(size) if (blob->length < (size)) { \
+ DEBUG(1,("Unexpected FILEINFO reply size %d for level %u - expected min of %d\n", \
+ (int)blob->length, parms->generic.level, (size))); \
+ return NT_STATUS_INFO_LENGTH_MISMATCH; \
+}
+#define FINFO_CHECK_SIZE(size) if (blob->length != (size)) { \
+ DEBUG(1,("Unexpected FILEINFO reply size %d for level %u - expected %d\n", \
+ (int)blob->length, parms->generic.level, (size))); \
+ return NT_STATUS_INFO_LENGTH_MISMATCH; \
+}
+
+/*
+ parse a stream information structure
+*/
+NTSTATUS smbcli_parse_stream_info(DATA_BLOB blob, TALLOC_CTX *mem_ctx,
+ struct stream_information *io)
+{
+ uint32_t ofs = 0;
+ io->num_streams = 0;
+ io->streams = NULL;
+
+ while (blob.length - ofs >= 24) {
+ uint_t n = io->num_streams;
+ uint32_t nlen, len;
+ bool ret;
+ void *vstr;
+ io->streams =
+ talloc_realloc(mem_ctx, io->streams, struct stream_struct, n+1);
+ if (!io->streams) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ nlen = IVAL(blob.data, ofs + 0x04);
+ io->streams[n].size = BVAL(blob.data, ofs + 0x08);
+ io->streams[n].alloc_size = BVAL(blob.data, ofs + 0x10);
+ if (nlen > blob.length - (ofs + 24)) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ ret = convert_string_talloc(io->streams,
+ CH_UTF16, CH_UNIX,
+ blob.data+ofs+24, nlen, &vstr, NULL, false);
+ if (!ret) {
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+ io->streams[n].stream_name.s = (const char *)vstr;
+ io->streams[n].stream_name.private_length = nlen;
+ io->num_streams++;
+ len = IVAL(blob.data, ofs);
+ if (len > blob.length - ofs) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ if (len == 0) break;
+ ofs += len;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ parse the fsinfo 'passthru' level replies
+*/
+NTSTATUS smb_raw_fileinfo_passthru_parse(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
+ enum smb_fileinfo_level level,
+ union smb_fileinfo *parms)
+{
+ switch (level) {
+ case RAW_FILEINFO_BASIC_INFORMATION:
+ /* some servers return 40 bytes and some 36. w2k3 return 40, so thats
+ what we should do, but we need to accept 36 */
+ if (blob->length != 36) {
+ FINFO_CHECK_SIZE(40);
+ }
+ parms->basic_info.out.create_time = smbcli_pull_nttime(blob->data, 0);
+ parms->basic_info.out.access_time = smbcli_pull_nttime(blob->data, 8);
+ parms->basic_info.out.write_time = smbcli_pull_nttime(blob->data, 16);
+ parms->basic_info.out.change_time = smbcli_pull_nttime(blob->data, 24);
+ parms->basic_info.out.attrib = IVAL(blob->data, 32);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STANDARD_INFORMATION:
+ FINFO_CHECK_SIZE(24);
+ parms->standard_info.out.alloc_size = BVAL(blob->data, 0);
+ parms->standard_info.out.size = BVAL(blob->data, 8);
+ parms->standard_info.out.nlink = IVAL(blob->data, 16);
+ parms->standard_info.out.delete_pending = CVAL(blob->data, 20);
+ parms->standard_info.out.directory = CVAL(blob->data, 21);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_INFORMATION:
+ FINFO_CHECK_SIZE(4);
+ parms->ea_info.out.ea_size = IVAL(blob->data, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NAME_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(4);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &parms->name_info.out.fname, 0, 4, STR_UNICODE);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALL_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(72);
+ parms->all_info.out.create_time = smbcli_pull_nttime(blob->data, 0);
+ parms->all_info.out.access_time = smbcli_pull_nttime(blob->data, 8);
+ parms->all_info.out.write_time = smbcli_pull_nttime(blob->data, 16);
+ parms->all_info.out.change_time = smbcli_pull_nttime(blob->data, 24);
+ parms->all_info.out.attrib = IVAL(blob->data, 32);
+ parms->all_info.out.alloc_size = BVAL(blob->data, 40);
+ parms->all_info.out.size = BVAL(blob->data, 48);
+ parms->all_info.out.nlink = IVAL(blob->data, 56);
+ parms->all_info.out.delete_pending = CVAL(blob->data, 60);
+ parms->all_info.out.directory = CVAL(blob->data, 61);
+#if 1
+ parms->all_info.out.ea_size = IVAL(blob->data, 64);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &parms->all_info.out.fname, 68, 72, STR_UNICODE);
+#else
+ /* this is what the CIFS spec says - and its totally
+ wrong, but its useful having it here so we can
+ quickly adapt to broken servers when running
+ tests */
+ parms->all_info.out.ea_size = IVAL(blob->data, 72);
+ /* access flags 4 bytes at 76
+ current_position 8 bytes at 80
+ mode 4 bytes at 88
+ alignment 4 bytes at 92
+ */
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &parms->all_info.out.fname, 96, 100, STR_UNICODE);
+#endif
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(4);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &parms->alt_name_info.out.fname, 0, 4, STR_UNICODE);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ return smbcli_parse_stream_info(*blob, mem_ctx, &parms->stream_info.out);
+
+ case RAW_FILEINFO_INTERNAL_INFORMATION:
+ FINFO_CHECK_SIZE(8);
+ parms->internal_information.out.file_id = BVAL(blob->data, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ FINFO_CHECK_SIZE(4);
+ parms->access_information.out.access_flags = IVAL(blob->data, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_POSITION_INFORMATION:
+ FINFO_CHECK_SIZE(8);
+ parms->position_information.out.position = BVAL(blob->data, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_MODE_INFORMATION:
+ FINFO_CHECK_SIZE(4);
+ parms->mode_information.out.mode = IVAL(blob->data, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+ FINFO_CHECK_SIZE(4);
+ parms->alignment_information.out.alignment_requirement
+ = IVAL(blob->data, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ FINFO_CHECK_SIZE(16);
+ parms->compression_info.out.compressed_size = BVAL(blob->data, 0);
+ parms->compression_info.out.format = SVAL(blob->data, 8);
+ parms->compression_info.out.unit_shift = CVAL(blob->data, 10);
+ parms->compression_info.out.chunk_shift = CVAL(blob->data, 11);
+ parms->compression_info.out.cluster_shift = CVAL(blob->data, 12);
+ /* 3 bytes of padding */
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ FINFO_CHECK_SIZE(56);
+ parms->network_open_information.out.create_time = smbcli_pull_nttime(blob->data, 0);
+ parms->network_open_information.out.access_time = smbcli_pull_nttime(blob->data, 8);
+ parms->network_open_information.out.write_time = smbcli_pull_nttime(blob->data, 16);
+ parms->network_open_information.out.change_time = smbcli_pull_nttime(blob->data, 24);
+ parms->network_open_information.out.alloc_size = BVAL(blob->data, 32);
+ parms->network_open_information.out.size = BVAL(blob->data, 40);
+ parms->network_open_information.out.attrib = IVAL(blob->data, 48);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ FINFO_CHECK_SIZE(8);
+ parms->attribute_tag_information.out.attrib = IVAL(blob->data, 0);
+ parms->attribute_tag_information.out.reparse_tag = IVAL(blob->data, 4);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_SMB2_ALL_EAS:
+ FINFO_CHECK_MIN_SIZE(4);
+ return ea_pull_list_chained(blob, mem_ctx,
+ &parms->all_eas.out.num_eas,
+ &parms->all_eas.out.eas);
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(0x64);
+ parms->all_info2.out.create_time = smbcli_pull_nttime(blob->data, 0x00);
+ parms->all_info2.out.access_time = smbcli_pull_nttime(blob->data, 0x08);
+ parms->all_info2.out.write_time = smbcli_pull_nttime(blob->data, 0x10);
+ parms->all_info2.out.change_time = smbcli_pull_nttime(blob->data, 0x18);
+ parms->all_info2.out.attrib = IVAL(blob->data, 0x20);
+ parms->all_info2.out.unknown1 = IVAL(blob->data, 0x24);
+ parms->all_info2.out.alloc_size = BVAL(blob->data, 0x28);
+ parms->all_info2.out.size = BVAL(blob->data, 0x30);
+ parms->all_info2.out.nlink = IVAL(blob->data, 0x38);
+ parms->all_info2.out.delete_pending = CVAL(blob->data, 0x3C);
+ parms->all_info2.out.directory = CVAL(blob->data, 0x3D);
+ /* 0x3E-0x3F padding */
+ parms->all_info2.out.file_id = BVAL(blob->data, 0x40);
+ parms->all_info2.out.ea_size = IVAL(blob->data, 0x48);
+ parms->all_info2.out.access_mask = IVAL(blob->data, 0x4C);
+ parms->all_info2.out.position = BVAL(blob->data, 0x50);
+ parms->all_info2.out.mode = IVAL(blob->data, 0x58);
+ parms->all_info2.out.alignment_requirement = IVAL(blob->data, 0x5C);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &parms->all_info2.out.fname, 0x60, 0x64, STR_UNICODE);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_SEC_DESC: {
+ enum ndr_err_code ndr_err;
+
+ parms->query_secdesc.out.sd = talloc(mem_ctx, struct security_descriptor);
+ NT_STATUS_HAVE_NO_MEMORY(parms->query_secdesc.out.sd);
+
+ ndr_err = ndr_pull_struct_blob(blob, mem_ctx, NULL,
+ parms->query_secdesc.out.sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+ }
+
+ default:
+ break;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+
+/****************************************************************************
+ Handle qfileinfo/qpathinfo trans2 backend.
+****************************************************************************/
+static NTSTATUS smb_raw_info_backend(struct smbcli_session *session,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms,
+ DATA_BLOB *blob)
+{
+ switch (parms->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ case RAW_FILEINFO_GETATTR:
+ case RAW_FILEINFO_GETATTRE:
+ case RAW_FILEINFO_SEC_DESC:
+ /* not handled here */
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_FILEINFO_STANDARD:
+ FINFO_CHECK_SIZE(22);
+ parms->standard.out.create_time = raw_pull_dos_date2(session->transport,
+ blob->data + 0);
+ parms->standard.out.access_time = raw_pull_dos_date2(session->transport,
+ blob->data + 4);
+ parms->standard.out.write_time = raw_pull_dos_date2(session->transport,
+ blob->data + 8);
+ parms->standard.out.size = IVAL(blob->data, 12);
+ parms->standard.out.alloc_size = IVAL(blob->data, 16);
+ parms->standard.out.attrib = SVAL(blob->data, 20);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_SIZE:
+ FINFO_CHECK_SIZE(26);
+ parms->ea_size.out.create_time = raw_pull_dos_date2(session->transport,
+ blob->data + 0);
+ parms->ea_size.out.access_time = raw_pull_dos_date2(session->transport,
+ blob->data + 4);
+ parms->ea_size.out.write_time = raw_pull_dos_date2(session->transport,
+ blob->data + 8);
+ parms->ea_size.out.size = IVAL(blob->data, 12);
+ parms->ea_size.out.alloc_size = IVAL(blob->data, 16);
+ parms->ea_size.out.attrib = SVAL(blob->data, 20);
+ parms->ea_size.out.ea_size = IVAL(blob->data, 22);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_LIST:
+ FINFO_CHECK_MIN_SIZE(4);
+ return ea_pull_list(blob, mem_ctx,
+ &parms->ea_list.out.num_eas,
+ &parms->ea_list.out.eas);
+
+ case RAW_FILEINFO_ALL_EAS:
+ FINFO_CHECK_MIN_SIZE(4);
+ return ea_pull_list(blob, mem_ctx,
+ &parms->all_eas.out.num_eas,
+ &parms->all_eas.out.eas);
+
+ case RAW_FILEINFO_IS_NAME_VALID:
+ /* no data! */
+ FINFO_CHECK_SIZE(0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_BASIC_INFO:
+ case RAW_FILEINFO_BASIC_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_BASIC_INFORMATION, parms);
+
+ case RAW_FILEINFO_STANDARD_INFO:
+ case RAW_FILEINFO_STANDARD_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_STANDARD_INFORMATION, parms);
+
+ case RAW_FILEINFO_EA_INFO:
+ case RAW_FILEINFO_EA_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_EA_INFORMATION, parms);
+
+ case RAW_FILEINFO_NAME_INFO:
+ case RAW_FILEINFO_NAME_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_NAME_INFORMATION, parms);
+
+ case RAW_FILEINFO_ALL_INFO:
+ case RAW_FILEINFO_ALL_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_ALL_INFORMATION, parms);
+
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_ALT_NAME_INFORMATION, parms);
+
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_STREAM_INFORMATION, parms);
+
+ case RAW_FILEINFO_INTERNAL_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_INTERNAL_INFORMATION, parms);
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_ACCESS_INFORMATION, parms);
+
+ case RAW_FILEINFO_POSITION_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_POSITION_INFORMATION, parms);
+
+ case RAW_FILEINFO_MODE_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_MODE_INFORMATION, parms);
+
+ case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_ALIGNMENT_INFORMATION, parms);
+
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_COMPRESSION_INFORMATION, parms);
+
+ case RAW_FILEINFO_UNIX_BASIC:
+ FINFO_CHECK_SIZE(100);
+ parms->unix_basic_info.out.end_of_file = BVAL(blob->data, 0);
+ parms->unix_basic_info.out.num_bytes = BVAL(blob->data, 8);
+ parms->unix_basic_info.out.status_change_time = smbcli_pull_nttime(blob->data, 16);
+ parms->unix_basic_info.out.access_time = smbcli_pull_nttime(blob->data, 24);
+ parms->unix_basic_info.out.change_time = smbcli_pull_nttime(blob->data, 32);
+ parms->unix_basic_info.out.uid = BVAL(blob->data, 40);
+ parms->unix_basic_info.out.gid = BVAL(blob->data, 48);
+ parms->unix_basic_info.out.file_type = IVAL(blob->data, 52);
+ parms->unix_basic_info.out.dev_major = BVAL(blob->data, 60);
+ parms->unix_basic_info.out.dev_minor = BVAL(blob->data, 68);
+ parms->unix_basic_info.out.unique_id = BVAL(blob->data, 76);
+ parms->unix_basic_info.out.permissions = BVAL(blob->data, 84);
+ parms->unix_basic_info.out.nlink = BVAL(blob->data, 92);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_UNIX_INFO2:
+ FINFO_CHECK_SIZE(116);
+ parms->unix_info2.out.end_of_file = BVAL(blob->data, 0);
+ parms->unix_info2.out.num_bytes = BVAL(blob->data, 8);
+ parms->unix_info2.out.status_change_time = smbcli_pull_nttime(blob->data, 16);
+ parms->unix_info2.out.access_time = smbcli_pull_nttime(blob->data, 24);
+ parms->unix_info2.out.change_time = smbcli_pull_nttime(blob->data, 32);
+ parms->unix_info2.out.uid = BVAL(blob->data, 40);
+ parms->unix_info2.out.gid = BVAL(blob->data, 48);
+ parms->unix_info2.out.file_type = IVAL(blob->data, 52);
+ parms->unix_info2.out.dev_major = BVAL(blob->data, 60);
+ parms->unix_info2.out.dev_minor = BVAL(blob->data, 68);
+ parms->unix_info2.out.unique_id = BVAL(blob->data, 76);
+ parms->unix_info2.out.permissions = BVAL(blob->data, 84);
+ parms->unix_info2.out.nlink = BVAL(blob->data, 92);
+ parms->unix_info2.out.create_time = smbcli_pull_nttime(blob->data, 100);
+ parms->unix_info2.out.file_flags = IVAL(blob->data, 108);
+ parms->unix_info2.out.flags_mask = IVAL(blob->data, 112);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_UNIX_LINK:
+ smbcli_blob_pull_string(session, mem_ctx, blob,
+ &parms->unix_link_info.out.link_dest, 0, 4, STR_UNICODE);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_NETWORK_OPEN_INFORMATION, parms);
+
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, parms);
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_SMB2_ALL_INFORMATION, parms);
+
+ case RAW_FILEINFO_SMB2_ALL_EAS:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_SMB2_ALL_EAS, parms);
+
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+
+/****************************************************************************
+ Very raw query file info - returns param/data blobs - (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_fileinfo_blob_send(struct smbcli_tree *tree,
+ uint16_t fnum,
+ uint16_t info_level,
+ DATA_BLOB data)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_QFILEINFO;
+ struct smbcli_request *req;
+ TALLOC_CTX *mem_ctx = talloc_init("raw_fileinfo");
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.data = data;
+ tp.in.max_param = 2;
+ tp.in.max_data = 0xFFFF;
+ tp.in.setup = &setup;
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 4);
+ if (!tp.in.params.data) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ SSVAL(tp.in.params.data, 0, fnum);
+ SSVAL(tp.in.params.data, 2, info_level);
+
+ req = smb_raw_trans2_send(tree, &tp);
+
+ talloc_free(mem_ctx);
+
+ return req;
+}
+
+
+/****************************************************************************
+ Very raw query file info - returns param/data blobs - (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_fileinfo_blob_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob)
+{
+ struct smb_trans2 tp;
+ NTSTATUS status = smb_raw_trans2_recv(req, mem_ctx, &tp);
+ if (NT_STATUS_IS_OK(status)) {
+ *blob = tp.out.data;
+ }
+ return status;
+}
+
+/****************************************************************************
+ Very raw query path info - returns param/data blobs (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_pathinfo_blob_send(struct smbcli_tree *tree,
+ const char *fname,
+ uint16_t info_level,
+ DATA_BLOB data)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_QPATHINFO;
+ struct smbcli_request *req;
+ TALLOC_CTX *mem_ctx = talloc_init("raw_pathinfo");
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.data = data;
+ tp.in.max_param = 2;
+ tp.in.max_data = 0xFFFF;
+ tp.in.setup = &setup;
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 6);
+ if (!tp.in.params.data) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ SSVAL(tp.in.params.data, 0, info_level);
+ SIVAL(tp.in.params.data, 2, 0);
+ smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
+ fname, STR_TERMINATE);
+
+ req = smb_raw_trans2_send(tree, &tp);
+
+ talloc_free(mem_ctx);
+
+ return req;
+}
+
+/****************************************************************************
+ send a SMBgetatr (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_getattr_send(struct smbcli_tree *tree,
+ union smb_fileinfo *parms)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBgetatr, 0, 0);
+ if (!req) return NULL;
+
+ smbcli_req_append_ascii4(req, parms->getattr.in.file.path, STR_TERMINATE);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ send a SMBgetatr (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_getattr_recv(struct smbcli_request *req,
+ union smb_fileinfo *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ SMBCLI_CHECK_WCT(req, 10);
+ parms->getattr.out.attrib = SVAL(req->in.vwv, VWV(0));
+ parms->getattr.out.write_time = raw_pull_dos_date3(req->transport,
+ req->in.vwv + VWV(1));
+ parms->getattr.out.size = IVAL(req->in.vwv, VWV(3));
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Handle SMBgetattrE (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_getattrE_send(struct smbcli_tree *tree,
+ union smb_fileinfo *parms)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBgetattrE, 1, 0);
+ if (!req) return NULL;
+
+ SSVAL(req->out.vwv, VWV(0), parms->getattre.in.file.fnum);
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Handle SMBgetattrE (async send)
+****************************************************************************/
+static NTSTATUS smb_raw_getattrE_recv(struct smbcli_request *req,
+ union smb_fileinfo *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ SMBCLI_CHECK_WCT(req, 11);
+ parms->getattre.out.create_time = raw_pull_dos_date2(req->transport,
+ req->in.vwv + VWV(0));
+ parms->getattre.out.access_time = raw_pull_dos_date2(req->transport,
+ req->in.vwv + VWV(2));
+ parms->getattre.out.write_time = raw_pull_dos_date2(req->transport,
+ req->in.vwv + VWV(4));
+ parms->getattre.out.size = IVAL(req->in.vwv, VWV(6));
+ parms->getattre.out.alloc_size = IVAL(req->in.vwv, VWV(8));
+ parms->getattre.out.attrib = SVAL(req->in.vwv, VWV(10));
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Query file info (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_fileinfo_send(struct smbcli_tree *tree,
+ union smb_fileinfo *parms)
+{
+ DATA_BLOB data;
+ struct smbcli_request *req;
+
+ /* pass off the non-trans2 level to specialised functions */
+ if (parms->generic.level == RAW_FILEINFO_GETATTRE) {
+ return smb_raw_getattrE_send(tree, parms);
+ }
+ if (parms->generic.level == RAW_FILEINFO_SEC_DESC) {
+ return smb_raw_query_secdesc_send(tree, parms);
+ }
+ if (parms->generic.level >= RAW_FILEINFO_GENERIC) {
+ return NULL;
+ }
+
+ data = data_blob(NULL, 0);
+
+ if (parms->generic.level == RAW_FILEINFO_EA_LIST) {
+ if (!ea_push_name_list(tree,
+ &data,
+ parms->ea_list.in.num_names,
+ parms->ea_list.in.ea_names)) {
+ return NULL;
+ }
+ }
+
+ req = smb_raw_fileinfo_blob_send(tree,
+ parms->generic.in.file.fnum,
+ parms->generic.level, data);
+
+ data_blob_free(&data);
+
+ return req;
+}
+
+/****************************************************************************
+ Query file info (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_fileinfo_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+ struct smbcli_session *session = req?req->session:NULL;
+
+ if (parms->generic.level == RAW_FILEINFO_GETATTRE) {
+ return smb_raw_getattrE_recv(req, parms);
+ }
+ if (parms->generic.level == RAW_FILEINFO_SEC_DESC) {
+ return smb_raw_query_secdesc_recv(req, mem_ctx, parms);
+ }
+ if (parms->generic.level == RAW_FILEINFO_GETATTR) {
+ return smb_raw_getattr_recv(req, parms);
+ }
+
+ status = smb_raw_fileinfo_blob_recv(req, mem_ctx, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return smb_raw_info_backend(session, mem_ctx, parms, &blob);
+}
+
+/****************************************************************************
+ Query file info (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_fileinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms)
+{
+ struct smbcli_request *req = smb_raw_fileinfo_send(tree, parms);
+ return smb_raw_fileinfo_recv(req, mem_ctx, parms);
+}
+
+/****************************************************************************
+ Query path info (async send)
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_pathinfo_send(struct smbcli_tree *tree,
+ union smb_fileinfo *parms)
+{
+ DATA_BLOB data;
+ struct smbcli_request *req;
+
+ if (parms->generic.level == RAW_FILEINFO_GETATTR) {
+ return smb_raw_getattr_send(tree, parms);
+ }
+ if (parms->generic.level >= RAW_FILEINFO_GENERIC) {
+ return NULL;
+ }
+
+ data = data_blob(NULL, 0);
+
+ if (parms->generic.level == RAW_FILEINFO_EA_LIST) {
+ if (!ea_push_name_list(tree,
+ &data,
+ parms->ea_list.in.num_names,
+ parms->ea_list.in.ea_names)) {
+ return NULL;
+ }
+ }
+
+ req = smb_raw_pathinfo_blob_send(tree, parms->generic.in.file.path,
+ parms->generic.level, data);
+ data_blob_free(&data);
+
+ return req;
+}
+
+/****************************************************************************
+ Query path info (async recv)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_pathinfo_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms)
+{
+ /* recv is idential to fileinfo */
+ return smb_raw_fileinfo_recv(req, mem_ctx, parms);
+}
+
+/****************************************************************************
+ Query path info (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_pathinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms)
+{
+ struct smbcli_request *req = smb_raw_pathinfo_send(tree, parms);
+ return smb_raw_pathinfo_recv(req, mem_ctx, parms);
+}
diff --git a/source4/libcli/raw/rawfsinfo.c b/source4/libcli/raw/rawfsinfo.c
new file mode 100644
index 0000000000..43a0919e38
--- /dev/null
+++ b/source4/libcli/raw/rawfsinfo.c
@@ -0,0 +1,336 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RAW_QFS_* operations
+
+ Copyright (C) Andrew Tridgell 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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+
+/****************************************************************************
+ Query FS Info - SMBdskattr call (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_dskattr_send(struct smbcli_tree *tree,
+ union smb_fsinfo *fsinfo)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBdskattr, 0, 0);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Query FS Info - SMBdskattr call (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_dskattr_recv(struct smbcli_request *req,
+ union smb_fsinfo *fsinfo)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ SMBCLI_CHECK_WCT(req, 5);
+ fsinfo->dskattr.out.units_total = SVAL(req->in.vwv, VWV(0));
+ fsinfo->dskattr.out.blocks_per_unit = SVAL(req->in.vwv, VWV(1));
+ fsinfo->dskattr.out.block_size = SVAL(req->in.vwv, VWV(2));
+ fsinfo->dskattr.out.units_free = SVAL(req->in.vwv, VWV(3));
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ RAW_QFS_ trans2 interface via blobs (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_qfsinfo_send(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ uint16_t info_level)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_QFSINFO;
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.max_param = 0;
+ tp.in.max_data = 0xFFFF;
+ tp.in.setup = &setup;
+ tp.in.data = data_blob(NULL, 0);
+ tp.in.timeout = 0;
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 2);
+ if (!tp.in.params.data) {
+ return NULL;
+ }
+ SSVAL(tp.in.params.data, 0, info_level);
+
+ return smb_raw_trans2_send(tree, &tp);
+}
+
+/****************************************************************************
+ RAW_QFS_ trans2 interface via blobs (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_qfsinfo_blob_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob)
+{
+ struct smb_trans2 tp;
+ NTSTATUS status;
+
+ status = smb_raw_trans2_recv(req, mem_ctx, &tp);
+
+ if (NT_STATUS_IS_OK(status)) {
+ (*blob) = tp.out.data;
+ }
+
+ return status;
+}
+
+
+/* local macros to make the code more readable */
+#define QFS_CHECK_MIN_SIZE(size) if (blob.length < (size)) { \
+ DEBUG(1,("Unexpected QFS reply size %d for level %u - expected min of %d\n", \
+ (int)blob.length, fsinfo->generic.level, (size))); \
+ status = NT_STATUS_INFO_LENGTH_MISMATCH; \
+ goto failed; \
+}
+#define QFS_CHECK_SIZE(size) if (blob.length != (size)) { \
+ DEBUG(1,("Unexpected QFS reply size %d for level %u - expected %d\n", \
+ (int)blob.length, fsinfo->generic.level, (size))); \
+ status = NT_STATUS_INFO_LENGTH_MISMATCH; \
+ goto failed; \
+}
+
+
+/****************************************************************************
+ Query FSInfo raw interface (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_fsinfo_send(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *fsinfo)
+{
+ uint16_t info_level;
+
+ /* handle the only non-trans2 call separately */
+ if (fsinfo->generic.level == RAW_QFS_DSKATTR) {
+ return smb_raw_dskattr_send(tree, fsinfo);
+ }
+ if (fsinfo->generic.level >= RAW_QFS_GENERIC) {
+ return NULL;
+ }
+
+ /* the headers map the trans2 levels direct to info levels */
+ info_level = (uint16_t)fsinfo->generic.level;
+
+ return smb_raw_qfsinfo_send(tree, mem_ctx, info_level);
+}
+
+/*
+ parse the fsinfo 'passthru' level replies
+*/
+NTSTATUS smb_raw_fsinfo_passthru_parse(DATA_BLOB blob, TALLOC_CTX *mem_ctx,
+ enum smb_fsinfo_level level,
+ union smb_fsinfo *fsinfo)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ enum ndr_err_code ndr_err;
+ int i;
+
+ /* parse the results */
+ switch (level) {
+ case RAW_QFS_VOLUME_INFORMATION:
+ QFS_CHECK_MIN_SIZE(18);
+ fsinfo->volume_info.out.create_time = smbcli_pull_nttime(blob.data, 0);
+ fsinfo->volume_info.out.serial_number = IVAL(blob.data, 8);
+ smbcli_blob_pull_string(NULL, mem_ctx, &blob,
+ &fsinfo->volume_info.out.volume_name,
+ 12, 18, STR_UNICODE);
+ break;
+
+ case RAW_QFS_SIZE_INFORMATION:
+ QFS_CHECK_SIZE(24);
+ fsinfo->size_info.out.total_alloc_units = BVAL(blob.data, 0);
+ fsinfo->size_info.out.avail_alloc_units = BVAL(blob.data, 8);
+ fsinfo->size_info.out.sectors_per_unit = IVAL(blob.data, 16);
+ fsinfo->size_info.out.bytes_per_sector = IVAL(blob.data, 20);
+ break;
+
+ case RAW_QFS_DEVICE_INFORMATION:
+ QFS_CHECK_SIZE(8);
+ fsinfo->device_info.out.device_type = IVAL(blob.data, 0);
+ fsinfo->device_info.out.characteristics = IVAL(blob.data, 4);
+ break;
+
+ case RAW_QFS_ATTRIBUTE_INFORMATION:
+ QFS_CHECK_MIN_SIZE(12);
+ fsinfo->attribute_info.out.fs_attr = IVAL(blob.data, 0);
+ fsinfo->attribute_info.out.max_file_component_length = IVAL(blob.data, 4);
+ smbcli_blob_pull_string(NULL, mem_ctx, &blob,
+ &fsinfo->attribute_info.out.fs_type,
+ 8, 12, STR_UNICODE);
+ break;
+
+ case RAW_QFS_QUOTA_INFORMATION:
+ QFS_CHECK_SIZE(48);
+ fsinfo->quota_information.out.unknown[0] = BVAL(blob.data, 0);
+ fsinfo->quota_information.out.unknown[1] = BVAL(blob.data, 8);
+ fsinfo->quota_information.out.unknown[2] = BVAL(blob.data, 16);
+ fsinfo->quota_information.out.quota_soft = BVAL(blob.data, 24);
+ fsinfo->quota_information.out.quota_hard = BVAL(blob.data, 32);
+ fsinfo->quota_information.out.quota_flags = BVAL(blob.data, 40);
+ break;
+
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ QFS_CHECK_SIZE(32);
+ fsinfo->full_size_information.out.total_alloc_units = BVAL(blob.data, 0);
+ fsinfo->full_size_information.out.call_avail_alloc_units = BVAL(blob.data, 8);
+ fsinfo->full_size_information.out.actual_avail_alloc_units = BVAL(blob.data, 16);
+ fsinfo->full_size_information.out.sectors_per_unit = IVAL(blob.data, 24);
+ fsinfo->full_size_information.out.bytes_per_sector = IVAL(blob.data, 28);
+ break;
+
+ case RAW_QFS_OBJECTID_INFORMATION:
+ QFS_CHECK_SIZE(64);
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, NULL, &fsinfo->objectid_information.out.guid,
+ (ndr_pull_flags_fn_t)ndr_pull_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ }
+ for (i=0;i<6;i++) {
+ fsinfo->objectid_information.out.unknown[i] = BVAL(blob.data, 16 + i*8);
+ }
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+failed:
+ return status;
+}
+
+
+/****************************************************************************
+ Query FSInfo raw interface (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_fsinfo_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *fsinfo)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+ struct smbcli_session *session = req?req->session:NULL;
+
+ if (fsinfo->generic.level == RAW_QFS_DSKATTR) {
+ return smb_raw_dskattr_recv(req, fsinfo);
+ }
+
+ status = smb_raw_qfsinfo_blob_recv(req, mem_ctx, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* parse the results */
+ switch (fsinfo->generic.level) {
+ case RAW_QFS_GENERIC:
+ case RAW_QFS_DSKATTR:
+ /* handled above */
+ break;
+
+ case RAW_QFS_ALLOCATION:
+ QFS_CHECK_SIZE(18);
+ fsinfo->allocation.out.fs_id = IVAL(blob.data, 0);
+ fsinfo->allocation.out.sectors_per_unit = IVAL(blob.data, 4);
+ fsinfo->allocation.out.total_alloc_units = IVAL(blob.data, 8);
+ fsinfo->allocation.out.avail_alloc_units = IVAL(blob.data, 12);
+ fsinfo->allocation.out.bytes_per_sector = SVAL(blob.data, 16);
+ break;
+
+ case RAW_QFS_VOLUME:
+ QFS_CHECK_MIN_SIZE(5);
+ fsinfo->volume.out.serial_number = IVAL(blob.data, 0);
+ smbcli_blob_pull_string(session, mem_ctx, &blob,
+ &fsinfo->volume.out.volume_name,
+ 4, 5, STR_LEN8BIT | STR_NOALIGN);
+ break;
+
+ case RAW_QFS_VOLUME_INFO:
+ case RAW_QFS_VOLUME_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_VOLUME_INFORMATION, fsinfo);
+
+ case RAW_QFS_SIZE_INFO:
+ case RAW_QFS_SIZE_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_SIZE_INFORMATION, fsinfo);
+
+ case RAW_QFS_DEVICE_INFO:
+ case RAW_QFS_DEVICE_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_DEVICE_INFORMATION, fsinfo);
+
+ case RAW_QFS_ATTRIBUTE_INFO:
+ case RAW_QFS_ATTRIBUTE_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_ATTRIBUTE_INFORMATION, fsinfo);
+
+ case RAW_QFS_UNIX_INFO:
+ QFS_CHECK_SIZE(12);
+ fsinfo->unix_info.out.major_version = SVAL(blob.data, 0);
+ fsinfo->unix_info.out.minor_version = SVAL(blob.data, 2);
+ fsinfo->unix_info.out.capability = SVAL(blob.data, 4);
+ break;
+
+ case RAW_QFS_QUOTA_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_QUOTA_INFORMATION, fsinfo);
+
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_FULL_SIZE_INFORMATION, fsinfo);
+
+ case RAW_QFS_OBJECTID_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_OBJECTID_INFORMATION, fsinfo);
+ }
+
+failed:
+ return status;
+}
+
+/****************************************************************************
+ Query FSInfo raw interface (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_fsinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *fsinfo)
+{
+ struct smbcli_request *req = smb_raw_fsinfo_send(tree, mem_ctx, fsinfo);
+ return smb_raw_fsinfo_recv(req, mem_ctx, fsinfo);
+}
diff --git a/source4/libcli/raw/rawioctl.c b/source4/libcli/raw/rawioctl.c
new file mode 100644
index 0000000000..77c7b03f15
--- /dev/null
+++ b/source4/libcli/raw/rawioctl.c
@@ -0,0 +1,173 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file operations
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+#define SETUP_REQUEST(cmd, wct, buflen) do { \
+ req = smbcli_request_setup(tree, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+/*
+ send a raw smb ioctl - async send
+*/
+static struct smbcli_request *smb_raw_smbioctl_send(struct smbcli_tree *tree,
+ union smb_ioctl *parms)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST(SMBioctl, 3, 0);
+
+ SSVAL(req->out.vwv, VWV(0), parms->ioctl.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(1), parms->ioctl.in.request);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/*
+ send a raw smb ioctl - async recv
+*/
+static NTSTATUS smb_raw_smbioctl_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_ioctl *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ parms->ioctl.out.blob = smbcli_req_pull_blob(&req->in.bufinfo, mem_ctx, req->in.data, -1);
+ return smbcli_request_destroy(req);
+}
+
+
+
+/****************************************************************************
+NT ioctl (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_ntioctl_send(struct smbcli_tree *tree,
+ union smb_ioctl *parms)
+{
+ struct smb_nttrans nt;
+ uint8_t setup[8];
+
+ nt.in.max_setup = 4;
+ nt.in.max_param = 0;
+ nt.in.max_data = parms->ntioctl.in.max_data;
+ nt.in.setup_count = 4;
+ nt.in.setup = setup;
+ SIVAL(setup, 0, parms->ntioctl.in.function);
+ SSVAL(setup, 4, parms->ntioctl.in.file.fnum);
+ SCVAL(setup, 6, parms->ntioctl.in.fsctl);
+ SCVAL(setup, 7, parms->ntioctl.in.filter);
+ nt.in.function = NT_TRANSACT_IOCTL;
+ nt.in.params = data_blob(NULL, 0);
+ nt.in.data = parms->ntioctl.in.blob;
+
+ return smb_raw_nttrans_send(tree, &nt);
+}
+
+/****************************************************************************
+NT ioctl (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_ntioctl_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_ioctl *parms)
+{
+ NTSTATUS status;
+ struct smb_nttrans nt;
+ TALLOC_CTX *tmp_mem;
+
+ tmp_mem = talloc_new(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
+
+ status = smb_raw_nttrans_recv(req, tmp_mem, &nt);
+ if (!NT_STATUS_IS_OK(status)) goto fail;
+
+ parms->ntioctl.out.blob = nt.out.data;
+ talloc_steal(mem_ctx, parms->ntioctl.out.blob.data);
+
+fail:
+ talloc_free(tmp_mem);
+ return status;
+}
+
+
+/*
+ send a raw ioctl - async send
+*/
+struct smbcli_request *smb_raw_ioctl_send(struct smbcli_tree *tree, union smb_ioctl *parms)
+{
+ struct smbcli_request *req = NULL;
+
+ switch (parms->generic.level) {
+ case RAW_IOCTL_IOCTL:
+ req = smb_raw_smbioctl_send(tree, parms);
+ break;
+
+ case RAW_IOCTL_NTIOCTL:
+ req = smb_raw_ntioctl_send(tree, parms);
+ break;
+
+ case RAW_IOCTL_SMB2:
+ case RAW_IOCTL_SMB2_NO_HANDLE:
+ return NULL;
+ }
+
+ return req;
+}
+
+/*
+ recv a raw ioctl - async recv
+*/
+NTSTATUS smb_raw_ioctl_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx, union smb_ioctl *parms)
+{
+ switch (parms->generic.level) {
+ case RAW_IOCTL_IOCTL:
+ return smb_raw_smbioctl_recv(req, mem_ctx, parms);
+
+ case RAW_IOCTL_NTIOCTL:
+ return smb_raw_ntioctl_recv(req, mem_ctx, parms);
+
+ case RAW_IOCTL_SMB2:
+ case RAW_IOCTL_SMB2_NO_HANDLE:
+ break;
+ }
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+ send a raw ioctl - sync interface
+*/
+NTSTATUS smb_raw_ioctl(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx, union smb_ioctl *parms)
+{
+ struct smbcli_request *req;
+ req = smb_raw_ioctl_send(tree, parms);
+ return smb_raw_ioctl_recv(req, mem_ctx, parms);
+}
diff --git a/source4/libcli/raw/rawlpq.c b/source4/libcli/raw/rawlpq.c
new file mode 100644
index 0000000000..eddb3e0843
--- /dev/null
+++ b/source4/libcli/raw/rawlpq.c
@@ -0,0 +1,49 @@
+/*
+ Unix SMB/CIFS implementation.
+ client lpq operations
+ Copyright (C) Tim Potter 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 "includes.h"
+#include "smb.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+/****************************************************************************
+ lpq - async send
+****************************************************************************/
+struct smbcli_request *smb_raw_lpq_send(struct smbcli_tree *tree,
+ union smb_lpq *parms)
+{
+ return NULL;
+}
+
+/****************************************************************************
+ lpq - async receive
+****************************************************************************/
+NTSTATUS smb_raw_lpq_recv(struct smbcli_request *req, union smb_lpq *parms)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ lpq - sync interface
+*/
+NTSTATUS smb_raw_lpq(struct smbcli_tree *tree, union smb_lpq *parms)
+{
+ struct smbcli_request *req = smb_raw_lpq_send(tree, parms);
+ return smb_raw_lpq_recv(req, parms);
+}
diff --git a/source4/libcli/raw/rawnegotiate.c b/source4/libcli/raw/rawnegotiate.c
new file mode 100644
index 0000000000..dedc891ac1
--- /dev/null
+++ b/source4/libcli/raw/rawnegotiate.c
@@ -0,0 +1,206 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB client negotiate context management functions
+
+ Copyright (C) Andrew Tridgell 1994-2005
+ Copyright (C) James Myers 2003 <myersjj@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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "system/time.h"
+
+static const struct {
+ enum protocol_types prot;
+ const char *name;
+} prots[] = {
+ {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"},
+ {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
+ {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
+ {PROTOCOL_LANMAN1,"LANMAN1.0"},
+ {PROTOCOL_LANMAN1,"Windows for Workgroups 3.1a"},
+ {PROTOCOL_LANMAN2,"LM1.2X002"},
+ {PROTOCOL_LANMAN2,"DOS LANMAN2.1"},
+ {PROTOCOL_LANMAN2,"LANMAN2.1"},
+ {PROTOCOL_LANMAN2,"Samba"},
+ {PROTOCOL_NT1,"NT LANMAN 1.0"},
+ {PROTOCOL_NT1,"NT LM 0.12"},
+#if 0
+ /* we don't yet handle chaining a SMB transport onto SMB2 */
+ {PROTOCOL_SMB2,"SMB 2.002"},
+#endif
+};
+
+/*
+ Send a negprot command.
+*/
+struct smbcli_request *smb_raw_negotiate_send(struct smbcli_transport *transport,
+ bool unicode,
+ int maxprotocol)
+{
+ struct smbcli_request *req;
+ int i;
+ uint16_t flags2 = 0;
+
+ req = smbcli_request_setup_transport(transport, SMBnegprot, 0, 0);
+ if (!req) {
+ return NULL;
+ }
+
+ flags2 |= FLAGS2_32_BIT_ERROR_CODES;
+ if (unicode) {
+ flags2 |= FLAGS2_UNICODE_STRINGS;
+ }
+ flags2 |= FLAGS2_EXTENDED_ATTRIBUTES;
+ flags2 |= FLAGS2_LONG_PATH_COMPONENTS;
+ flags2 |= FLAGS2_IS_LONG_NAME;
+
+ if (transport->options.use_spnego) {
+ flags2 |= FLAGS2_EXTENDED_SECURITY;
+ }
+
+ SSVAL(req->out.hdr,HDR_FLG2, flags2);
+
+ /* setup the protocol strings */
+ for (i=0; i < ARRAY_SIZE(prots) && prots[i].prot <= maxprotocol; i++) {
+ smbcli_req_append_bytes(req, (const uint8_t *)"\2", 1);
+ smbcli_req_append_string(req, prots[i].name, STR_TERMINATE | STR_ASCII);
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/*
+ Send a negprot command.
+*/
+NTSTATUS smb_raw_negotiate_recv(struct smbcli_request *req)
+{
+ struct smbcli_transport *transport = req->transport;
+ int protocol;
+
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ SMBCLI_CHECK_MIN_WCT(req, 1);
+
+ protocol = SVALS(req->in.vwv, VWV(0));
+
+ if (protocol >= ARRAY_SIZE(prots) || protocol < 0) {
+ req->status = NT_STATUS_UNSUCCESSFUL;
+ return smbcli_request_destroy(req);
+ }
+
+ transport->negotiate.protocol = prots[protocol].prot;
+
+ if (transport->negotiate.protocol >= PROTOCOL_NT1) {
+ NTTIME ntt;
+
+ /* NT protocol */
+ SMBCLI_CHECK_WCT(req, 17);
+ transport->negotiate.sec_mode = CVAL(req->in.vwv,VWV(1));
+ transport->negotiate.max_mux = SVAL(req->in.vwv,VWV(1)+1);
+ transport->negotiate.max_xmit = IVAL(req->in.vwv,VWV(3)+1);
+ transport->negotiate.sesskey = IVAL(req->in.vwv,VWV(7)+1);
+ transport->negotiate.capabilities = IVAL(req->in.vwv,VWV(9)+1);
+
+ /* this time arrives in real GMT */
+ ntt = smbcli_pull_nttime(req->in.vwv, VWV(11)+1);
+ transport->negotiate.server_time = nt_time_to_unix(ntt);
+ transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(15)+1) * 60;
+ transport->negotiate.key_len = CVAL(req->in.vwv,VWV(16)+1);
+
+ if (transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) {
+ if (req->in.data_size < 16) {
+ goto failed;
+ }
+ transport->negotiate.server_guid = smbcli_req_pull_blob(&req->in.bufinfo, transport, req->in.data, 16);
+ transport->negotiate.secblob = smbcli_req_pull_blob(&req->in.bufinfo, transport, req->in.data + 16, req->in.data_size - 16);
+ } else {
+ if (req->in.data_size < (transport->negotiate.key_len)) {
+ goto failed;
+ }
+ transport->negotiate.secblob = smbcli_req_pull_blob(&req->in.bufinfo, transport, req->in.data, transport->negotiate.key_len);
+ smbcli_req_pull_string(&req->in.bufinfo, transport, &transport->negotiate.server_domain,
+ req->in.data+transport->negotiate.key_len,
+ req->in.data_size-transport->negotiate.key_len, STR_UNICODE|STR_NOALIGN);
+ /* here comes the server name */
+ }
+
+ if (transport->negotiate.capabilities & CAP_RAW_MODE) {
+ transport->negotiate.readbraw_supported = true;
+ transport->negotiate.writebraw_supported = true;
+ }
+ } else if (transport->negotiate.protocol >= PROTOCOL_LANMAN1) {
+ SMBCLI_CHECK_WCT(req, 13);
+ transport->negotiate.sec_mode = SVAL(req->in.vwv,VWV(1));
+ transport->negotiate.max_xmit = SVAL(req->in.vwv,VWV(2));
+ transport->negotiate.sesskey = IVAL(req->in.vwv,VWV(6));
+ transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(10)) * 60;
+
+ /* this time is converted to GMT by raw_pull_dos_date */
+ transport->negotiate.server_time = raw_pull_dos_date(transport,
+ req->in.vwv+VWV(8));
+ if ((SVAL(req->in.vwv,VWV(5)) & 0x1)) {
+ transport->negotiate.readbraw_supported = 1;
+ }
+ if ((SVAL(req->in.vwv,VWV(5)) & 0x2)) {
+ transport->negotiate.writebraw_supported = 1;
+ }
+ transport->negotiate.secblob = smbcli_req_pull_blob(&req->in.bufinfo, transport,
+ req->in.data, req->in.data_size);
+ } else {
+ /* the old core protocol */
+ transport->negotiate.sec_mode = 0;
+ transport->negotiate.server_time = time(NULL);
+ transport->negotiate.max_xmit = transport->options.max_xmit;
+ transport->negotiate.server_zone = get_time_zone(transport->negotiate.server_time);
+ }
+
+ /* a way to force ascii SMB */
+ if (!transport->options.unicode) {
+ transport->negotiate.capabilities &= ~CAP_UNICODE;
+ }
+
+ if (!transport->options.ntstatus_support) {
+ transport->negotiate.capabilities &= ~CAP_STATUS32;
+ }
+
+ if (!transport->options.use_level2_oplocks) {
+ transport->negotiate.capabilities &= ~CAP_LEVEL_II_OPLOCKS;
+ }
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+
+/*
+ Send a negprot command (sync interface)
+*/
+NTSTATUS smb_raw_negotiate(struct smbcli_transport *transport, bool unicode, int maxprotocol)
+{
+ struct smbcli_request *req = smb_raw_negotiate_send(transport, unicode, maxprotocol);
+ return smb_raw_negotiate_recv(req);
+}
diff --git a/source4/libcli/raw/rawnotify.c b/source4/libcli/raw/rawnotify.c
new file mode 100644
index 0000000000..28ab3eb1a4
--- /dev/null
+++ b/source4/libcli/raw/rawnotify.c
@@ -0,0 +1,168 @@
+/*
+ Unix SMB/CIFS implementation.
+ client change notify operations
+ Copyright (C) Andrew Tridgell 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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "../lib/util/dlinklist.h"
+
+/****************************************************************************
+change notify (async send)
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_changenotify_send(struct smbcli_tree *tree, union smb_notify *parms)
+{
+ struct smb_nttrans nt;
+ uint8_t setup[8];
+
+ if (parms->nttrans.level != RAW_NOTIFY_NTTRANS) {
+ return NULL;
+ }
+
+ nt.in.max_setup = 0;
+ nt.in.max_param = parms->nttrans.in.buffer_size;
+ nt.in.max_data = 0;
+ nt.in.setup_count = 4;
+ nt.in.setup = setup;
+ SIVAL(setup, 0, parms->nttrans.in.completion_filter);
+ SSVAL(setup, 4, parms->nttrans.in.file.fnum);
+ SSVAL(setup, 6, parms->nttrans.in.recursive);
+ nt.in.function = NT_TRANSACT_NOTIFY_CHANGE;
+ nt.in.params = data_blob(NULL, 0);
+ nt.in.data = data_blob(NULL, 0);
+
+ return smb_raw_nttrans_send(tree, &nt);
+}
+
+/****************************************************************************
+change notify (async recv)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_changenotify_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx, union smb_notify *parms)
+{
+ struct smb_nttrans nt;
+ NTSTATUS status;
+ uint32_t ofs, i;
+ struct smbcli_session *session = req?req->session:NULL;
+
+ if (parms->nttrans.level != RAW_NOTIFY_NTTRANS) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ status = smb_raw_nttrans_recv(req, mem_ctx, &nt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ parms->nttrans.out.changes = NULL;
+ parms->nttrans.out.num_changes = 0;
+
+ /* count them */
+ for (ofs=0; nt.out.params.length - ofs > 12; ) {
+ uint32_t next = IVAL(nt.out.params.data, ofs);
+ parms->nttrans.out.num_changes++;
+ if (next == 0 ||
+ ofs + next >= nt.out.params.length) break;
+ ofs += next;
+ }
+
+ /* allocate array */
+ parms->nttrans.out.changes = talloc_array(mem_ctx, struct notify_changes, parms->nttrans.out.num_changes);
+ if (!parms->nttrans.out.changes) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=ofs=0; i<parms->nttrans.out.num_changes; i++) {
+ parms->nttrans.out.changes[i].action = IVAL(nt.out.params.data, ofs+4);
+ smbcli_blob_pull_string(session, mem_ctx, &nt.out.params,
+ &parms->nttrans.out.changes[i].name,
+ ofs+8, ofs+12, STR_UNICODE);
+ ofs += IVAL(nt.out.params.data, ofs);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ handle ntcancel replies from the server,
+ as the MID of the real reply and the ntcancel reply is the same
+ we need to do find out to what request the reply belongs
+****************************************************************************/
+struct smbcli_request *smbcli_handle_ntcancel_reply(struct smbcli_request *req,
+ size_t len, const uint8_t *hdr)
+{
+ struct smbcli_request *ntcancel;
+
+ if (!req) return req;
+
+ if (!req->ntcancel) return req;
+
+ if (len >= MIN_SMB_SIZE + NBT_HDR_SIZE &&
+ (CVAL(hdr, HDR_FLG) & FLAG_REPLY) &&
+ CVAL(hdr,HDR_COM) == SMBntcancel) {
+ ntcancel = req->ntcancel;
+ DLIST_REMOVE(req->ntcancel, ntcancel);
+
+ /*
+ * TODO: untill we understand how the
+ * smb_signing works for this case we
+ * return NULL, to just ignore the packet
+ */
+ /*return ntcancel;*/
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Send a NT Cancel request - used to hurry along a pending request. Usually
+ used to cancel a pending change notify request
+ note that this request does not expect a response!
+****************************************************************************/
+NTSTATUS smb_raw_ntcancel(struct smbcli_request *oldreq)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup_transport(oldreq->transport, SMBntcancel, 0, 0);
+
+ SSVAL(req->out.hdr, HDR_MID, SVAL(oldreq->out.hdr, HDR_MID));
+ SSVAL(req->out.hdr, HDR_PID, SVAL(oldreq->out.hdr, HDR_PID));
+ SSVAL(req->out.hdr, HDR_TID, SVAL(oldreq->out.hdr, HDR_TID));
+ SSVAL(req->out.hdr, HDR_UID, SVAL(oldreq->out.hdr, HDR_UID));
+
+ /* this request does not expect a reply, so tell the signing
+ subsystem not to allocate an id for a reply */
+ req->sign_single_increment = 1;
+ req->one_way_request = 1;
+
+ /*
+ * smbcli_request_send() free's oneway requests
+ * but we want to keep it under oldreq->ntcancel
+ */
+ if (!talloc_reference(oldreq, req)) {
+ talloc_free(req);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ smbcli_request_send(req);
+
+ DLIST_ADD_END(oldreq->ntcancel, req, struct smbcli_request *);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/raw/rawreadwrite.c b/source4/libcli/raw/rawreadwrite.c
new file mode 100644
index 0000000000..a8c7996310
--- /dev/null
+++ b/source4/libcli/raw/rawreadwrite.c
@@ -0,0 +1,349 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file read/write routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) James Myers 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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+#define SETUP_REQUEST(cmd, wct, buflen) do { \
+ req = smbcli_request_setup(tree, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+/****************************************************************************
+ low level read operation (async send)
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_read_send(struct smbcli_tree *tree, union smb_read *parms)
+{
+ bool bigoffset = false;
+ struct smbcli_request *req = NULL;
+
+ switch (parms->generic.level) {
+ case RAW_READ_READBRAW:
+ if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ bigoffset = true;
+ }
+ SETUP_REQUEST(SMBreadbraw, bigoffset? 10:8, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->readbraw.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(1), parms->readbraw.in.offset);
+ SSVAL(req->out.vwv, VWV(3), parms->readbraw.in.maxcnt);
+ SSVAL(req->out.vwv, VWV(4), parms->readbraw.in.mincnt);
+ SIVAL(req->out.vwv, VWV(5), parms->readbraw.in.timeout);
+ SSVAL(req->out.vwv, VWV(7), 0); /* reserved */
+ if (bigoffset) {
+ SIVAL(req->out.vwv, VWV(8),parms->readbraw.in.offset>>32);
+ }
+ break;
+
+ case RAW_READ_LOCKREAD:
+ SETUP_REQUEST(SMBlockread, 5, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->lockread.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->lockread.in.count);
+ SIVAL(req->out.vwv, VWV(2), parms->lockread.in.offset);
+ SSVAL(req->out.vwv, VWV(4), parms->lockread.in.remaining);
+ break;
+
+ case RAW_READ_READ:
+ SETUP_REQUEST(SMBread, 5, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->read.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->read.in.count);
+ SIVAL(req->out.vwv, VWV(2), parms->read.in.offset);
+ SSVAL(req->out.vwv, VWV(4), parms->read.in.remaining);
+ break;
+
+ case RAW_READ_READX:
+ if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ bigoffset = true;
+ }
+ SETUP_REQUEST(SMBreadX, bigoffset ? 12 : 10, 0);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->readx.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(3), parms->readx.in.offset);
+ SSVAL(req->out.vwv, VWV(5), parms->readx.in.maxcnt & 0xFFFF);
+ SSVAL(req->out.vwv, VWV(6), parms->readx.in.mincnt);
+ SIVAL(req->out.vwv, VWV(7), parms->readx.in.maxcnt >> 16);
+ SSVAL(req->out.vwv, VWV(9), parms->readx.in.remaining);
+ /*
+ * TODO: give an error when the offset is 64 bit
+ * and the server doesn't support it
+ */
+ if (bigoffset) {
+ SIVAL(req->out.vwv, VWV(10),parms->readx.in.offset>>32);
+ }
+ if (parms->readx.in.read_for_execute) {
+ uint16_t flags2 = SVAL(req->out.hdr, HDR_FLG2);
+ flags2 |= FLAGS2_READ_PERMIT_EXECUTE;
+ SSVAL(req->out.hdr, HDR_FLG2, flags2);
+ }
+ break;
+
+ case RAW_READ_SMB2:
+ return NULL;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ /* the transport layer needs to know that a readbraw is pending
+ and handle receives a little differently */
+ if (parms->generic.level == RAW_READ_READBRAW) {
+ tree->session->transport->readbraw_pending = 1;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ low level read operation (async recv)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_read_recv(struct smbcli_request *req, union smb_read *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ switch (parms->generic.level) {
+ case RAW_READ_READBRAW:
+ parms->readbraw.out.nread = req->in.size - NBT_HDR_SIZE;
+ if (parms->readbraw.out.nread >
+ MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt)) {
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+ memcpy(parms->readbraw.out.data, req->in.buffer + NBT_HDR_SIZE, parms->readbraw.out.nread);
+ break;
+
+ case RAW_READ_LOCKREAD:
+ SMBCLI_CHECK_WCT(req, 5);
+ parms->lockread.out.nread = SVAL(req->in.vwv, VWV(0));
+ if (parms->lockread.out.nread > parms->lockread.in.count ||
+ !smbcli_raw_pull_data(&req->in.bufinfo, req->in.data+3,
+ parms->lockread.out.nread, parms->lockread.out.data)) {
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+
+ case RAW_READ_READ:
+ /* there are 4 reserved words in the reply */
+ SMBCLI_CHECK_WCT(req, 5);
+ parms->read.out.nread = SVAL(req->in.vwv, VWV(0));
+ if (parms->read.out.nread > parms->read.in.count ||
+ !smbcli_raw_pull_data(&req->in.bufinfo, req->in.data+3,
+ parms->read.out.nread, parms->read.out.data)) {
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+
+ case RAW_READ_READX:
+ /* there are 5 reserved words in the reply */
+ SMBCLI_CHECK_WCT(req, 12);
+ parms->readx.out.remaining = SVAL(req->in.vwv, VWV(2));
+ parms->readx.out.compaction_mode = SVAL(req->in.vwv, VWV(3));
+ parms->readx.out.nread = SVAL(req->in.vwv, VWV(5));
+
+ /* handle oversize replies for non-chained readx replies with
+ CAP_LARGE_READX. The snia spec has must to answer for. */
+ if ((req->tree->session->transport->negotiate.capabilities & CAP_LARGE_READX)
+ && CVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE &&
+ req->in.size >= 0x10000) {
+ parms->readx.out.nread += (SVAL(req->in.vwv, VWV(7)) << 16);
+ if (req->in.hdr + SVAL(req->in.vwv, VWV(6)) +
+ parms->readx.out.nread <=
+ req->in.buffer + req->in.size) {
+ req->in.data_size += (SVAL(req->in.vwv, VWV(7)) << 16);
+
+ /* update the bufinfo with the new size */
+ smb_setup_bufinfo(req);
+ }
+ }
+
+ if (parms->readx.out.nread > MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt) ||
+ !smbcli_raw_pull_data(&req->in.bufinfo, req->in.hdr + SVAL(req->in.vwv, VWV(6)),
+ parms->readx.out.nread,
+ parms->readx.out.data)) {
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+
+ case RAW_READ_SMB2:
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+/****************************************************************************
+ low level read operation (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_read(struct smbcli_tree *tree, union smb_read *parms)
+{
+ struct smbcli_request *req = smb_raw_read_send(tree, parms);
+ return smb_raw_read_recv(req, parms);
+}
+
+
+/****************************************************************************
+ raw write interface (async send)
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_write_send(struct smbcli_tree *tree, union smb_write *parms)
+{
+ bool bigoffset = false;
+ struct smbcli_request *req = NULL;
+
+ switch (parms->generic.level) {
+ case RAW_WRITE_WRITEUNLOCK:
+ SETUP_REQUEST(SMBwriteunlock, 5, 3 + parms->writeunlock.in.count);
+ SSVAL(req->out.vwv, VWV(0), parms->writeunlock.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->writeunlock.in.count);
+ SIVAL(req->out.vwv, VWV(2), parms->writeunlock.in.offset);
+ SSVAL(req->out.vwv, VWV(4), parms->writeunlock.in.remaining);
+ SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+ SSVAL(req->out.data, 1, parms->writeunlock.in.count);
+ if (parms->writeunlock.in.count > 0) {
+ memcpy(req->out.data+3, parms->writeunlock.in.data,
+ parms->writeunlock.in.count);
+ }
+ break;
+
+ case RAW_WRITE_WRITE:
+ SETUP_REQUEST(SMBwrite, 5, 3 + parms->write.in.count);
+ SSVAL(req->out.vwv, VWV(0), parms->write.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->write.in.count);
+ SIVAL(req->out.vwv, VWV(2), parms->write.in.offset);
+ SSVAL(req->out.vwv, VWV(4), parms->write.in.remaining);
+ SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+ SSVAL(req->out.data, 1, parms->write.in.count);
+ if (parms->write.in.count > 0) {
+ memcpy(req->out.data+3, parms->write.in.data, parms->write.in.count);
+ }
+ break;
+
+ case RAW_WRITE_WRITECLOSE:
+ SETUP_REQUEST(SMBwriteclose, 6, 1 + parms->writeclose.in.count);
+ SSVAL(req->out.vwv, VWV(0), parms->writeclose.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->writeclose.in.count);
+ SIVAL(req->out.vwv, VWV(2), parms->writeclose.in.offset);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(4), parms->writeclose.in.mtime);
+ SCVAL(req->out.data, 0, 0);
+ if (parms->writeclose.in.count > 0) {
+ memcpy(req->out.data+1, parms->writeclose.in.data,
+ parms->writeclose.in.count);
+ }
+ break;
+
+ case RAW_WRITE_WRITEX:
+ if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ bigoffset = true;
+ }
+ SETUP_REQUEST(SMBwriteX, bigoffset ? 14 : 12, parms->writex.in.count);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->writex.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(3), parms->writex.in.offset);
+ SIVAL(req->out.vwv, VWV(5), 0); /* reserved */
+ SSVAL(req->out.vwv, VWV(7), parms->writex.in.wmode);
+ SSVAL(req->out.vwv, VWV(8), parms->writex.in.remaining);
+ SSVAL(req->out.vwv, VWV(9), parms->writex.in.count>>16);
+ SSVAL(req->out.vwv, VWV(10), parms->writex.in.count);
+ SSVAL(req->out.vwv, VWV(11), PTR_DIFF(req->out.data, req->out.hdr));
+ if (bigoffset) {
+ SIVAL(req->out.vwv,VWV(12),parms->writex.in.offset>>32);
+ }
+ if (parms->writex.in.count > 0) {
+ memcpy(req->out.data, parms->writex.in.data, parms->writex.in.count);
+ }
+ break;
+
+ case RAW_WRITE_SPLWRITE:
+ SETUP_REQUEST(SMBsplwr, 1, parms->splwrite.in.count);
+ SSVAL(req->out.vwv, VWV(0), parms->splwrite.in.file.fnum);
+ if (parms->splwrite.in.count > 0) {
+ memcpy(req->out.data, parms->splwrite.in.data, parms->splwrite.in.count);
+ }
+ break;
+
+ case RAW_WRITE_SMB2:
+ return NULL;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ raw write interface (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_write_recv(struct smbcli_request *req, union smb_write *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ switch (parms->generic.level) {
+ case RAW_WRITE_WRITEUNLOCK:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->writeunlock.out.nwritten = SVAL(req->in.vwv, VWV(0));
+ break;
+ case RAW_WRITE_WRITE:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->write.out.nwritten = SVAL(req->in.vwv, VWV(0));
+ break;
+ case RAW_WRITE_WRITECLOSE:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->writeclose.out.nwritten = SVAL(req->in.vwv, VWV(0));
+ break;
+ case RAW_WRITE_WRITEX:
+ SMBCLI_CHECK_WCT(req, 6);
+ parms->writex.out.nwritten = SVAL(req->in.vwv, VWV(2));
+ parms->writex.out.nwritten += (CVAL(req->in.vwv, VWV(4)) << 16);
+ parms->writex.out.remaining = SVAL(req->in.vwv, VWV(3));
+ break;
+ case RAW_WRITE_SPLWRITE:
+ break;
+ case RAW_WRITE_SMB2:
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+/****************************************************************************
+ raw write interface (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_write(struct smbcli_tree *tree, union smb_write *parms)
+{
+ struct smbcli_request *req = smb_raw_write_send(tree, parms);
+ return smb_raw_write_recv(req, parms);
+}
diff --git a/source4/libcli/raw/rawrequest.c b/source4/libcli/raw/rawrequest.c
new file mode 100644
index 0000000000..0f04190a8b
--- /dev/null
+++ b/source4/libcli/raw/rawrequest.c
@@ -0,0 +1,1029 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James Myers 2003 <myersjj@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/>.
+*/
+
+/*
+ this file implements functions for manipulating the 'struct smbcli_request' structure in libsmb
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+
+/* we over allocate the data buffer to prevent too many realloc calls */
+#define REQ_OVER_ALLOCATION 0
+
+/* assume that a character will not consume more than 3 bytes per char */
+#define MAX_BYTES_PER_CHAR 3
+
+/* setup the bufinfo used for strings and range checking */
+void smb_setup_bufinfo(struct smbcli_request *req)
+{
+ req->in.bufinfo.mem_ctx = req;
+ req->in.bufinfo.flags = 0;
+ if (req->flags2 & FLAGS2_UNICODE_STRINGS) {
+ req->in.bufinfo.flags = BUFINFO_FLAG_UNICODE;
+ }
+ req->in.bufinfo.align_base = req->in.buffer;
+ req->in.bufinfo.data = req->in.data;
+ req->in.bufinfo.data_size = req->in.data_size;
+}
+
+
+/* destroy a request structure and return final status */
+_PUBLIC_ NTSTATUS smbcli_request_destroy(struct smbcli_request *req)
+{
+ NTSTATUS status;
+
+ /* this is the error code we give the application for when a
+ _send() call fails completely */
+ if (!req) return NT_STATUS_UNSUCCESSFUL;
+
+ if (req->transport) {
+ /* remove it from the list of pending requests (a null op if
+ its not in the list) */
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+
+ if (req->state == SMBCLI_REQUEST_ERROR &&
+ NT_STATUS_IS_OK(req->status)) {
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = req->status;
+
+ if (!req->do_not_free) {
+ talloc_free(req);
+ }
+
+ return status;
+}
+
+
+/*
+ low-level function to setup a request buffer for a non-SMB packet
+ at the transport level
+*/
+struct smbcli_request *smbcli_request_setup_nonsmb(struct smbcli_transport *transport, size_t size)
+{
+ struct smbcli_request *req;
+
+ req = talloc(transport, struct smbcli_request);
+ if (!req) {
+ return NULL;
+ }
+ ZERO_STRUCTP(req);
+
+ /* setup the request context */
+ req->state = SMBCLI_REQUEST_INIT;
+ req->transport = transport;
+ req->session = NULL;
+ req->tree = NULL;
+ req->out.size = size;
+
+ /* over allocate by a small amount */
+ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
+
+ req->out.buffer = talloc_array(req, uint8_t, req->out.allocated);
+ if (!req->out.buffer) {
+ return NULL;
+ }
+
+ SIVAL(req->out.buffer, 0, 0);
+
+ return req;
+}
+
+
+/*
+ setup a SMB packet at transport level
+*/
+struct smbcli_request *smbcli_request_setup_transport(struct smbcli_transport *transport,
+ uint8_t command, uint_t wct, uint_t buflen)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup_nonsmb(transport, NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen);
+
+ if (!req) return NULL;
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.vwv = req->out.hdr + HDR_VWV;
+ req->out.wct = wct;
+ req->out.data = req->out.vwv + VWV(wct) + 2;
+ req->out.data_size = buflen;
+ req->out.ptr = req->out.data;
+
+ SCVAL(req->out.hdr, HDR_WCT, wct);
+ SSVAL(req->out.vwv, VWV(wct), buflen);
+
+ memcpy(req->out.hdr, "\377SMB", 4);
+ SCVAL(req->out.hdr,HDR_COM,command);
+
+ SCVAL(req->out.hdr,HDR_FLG, FLAG_CASELESS_PATHNAMES);
+ SSVAL(req->out.hdr,HDR_FLG2, 0);
+
+ if (command != SMBtranss && command != SMBtranss2) {
+ /* assign a mid */
+ req->mid = smbcli_transport_next_mid(transport);
+ }
+
+ /* copy the pid, uid and mid to the request */
+ SSVAL(req->out.hdr, HDR_PID, 0);
+ SSVAL(req->out.hdr, HDR_UID, 0);
+ SSVAL(req->out.hdr, HDR_MID, req->mid);
+ SSVAL(req->out.hdr, HDR_TID,0);
+ SSVAL(req->out.hdr, HDR_PIDHIGH,0);
+ SIVAL(req->out.hdr, HDR_RCLS, 0);
+ memset(req->out.hdr+HDR_SS_FIELD, 0, 10);
+
+ return req;
+}
+
+/*
+ setup a reply in req->out with the given word count and initial data
+ buffer size. the caller will then fill in the command words and
+ data before calling smbcli_request_send() to send the reply on its
+ way. This interface is used before a session is setup.
+*/
+struct smbcli_request *smbcli_request_setup_session(struct smbcli_session *session,
+ uint8_t command, uint_t wct, size_t buflen)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup_transport(session->transport, command, wct, buflen);
+
+ if (!req) return NULL;
+
+ req->session = session;
+
+ SSVAL(req->out.hdr, HDR_FLG2, session->flags2);
+ SSVAL(req->out.hdr, HDR_PID, session->pid & 0xFFFF);
+ SSVAL(req->out.hdr, HDR_PIDHIGH, session->pid >> 16);
+ SSVAL(req->out.hdr, HDR_UID, session->vuid);
+
+ return req;
+}
+
+/*
+ setup a request for tree based commands
+*/
+struct smbcli_request *smbcli_request_setup(struct smbcli_tree *tree,
+ uint8_t command,
+ uint_t wct, uint_t buflen)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup_session(tree->session, command, wct, buflen);
+ if (req) {
+ req->tree = tree;
+ SSVAL(req->out.hdr,HDR_TID,tree->tid);
+ }
+ return req;
+}
+
+
+/*
+ grow the allocation of the data buffer portion of a reply
+ packet. Note that as this can reallocate the packet buffer this
+ invalidates any local pointers into the packet.
+
+ To cope with this req->out.ptr is supplied. This will be updated to
+ point at the same offset into the packet as before this call
+*/
+static void smbcli_req_grow_allocation(struct smbcli_request *req, uint_t new_size)
+{
+ int delta;
+ uint8_t *buf2;
+
+ delta = new_size - req->out.data_size;
+ if (delta + req->out.size <= req->out.allocated) {
+ /* it fits in the preallocation */
+ return;
+ }
+
+ /* we need to realloc */
+ req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION;
+ buf2 = talloc_realloc(req, req->out.buffer, uint8_t, req->out.allocated);
+ if (buf2 == NULL) {
+ smb_panic("out of memory in req_grow_allocation");
+ }
+
+ if (buf2 == req->out.buffer) {
+ /* the malloc library gave us the same pointer */
+ return;
+ }
+
+ /* update the pointers into the packet */
+ req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer);
+ req->out.ptr = buf2 + PTR_DIFF(req->out.ptr, req->out.buffer);
+ req->out.vwv = buf2 + PTR_DIFF(req->out.vwv, req->out.buffer);
+ req->out.hdr = buf2 + PTR_DIFF(req->out.hdr, req->out.buffer);
+
+ req->out.buffer = buf2;
+}
+
+
+/*
+ grow the data buffer portion of a reply packet. Note that as this
+ can reallocate the packet buffer this invalidates any local pointers
+ into the packet.
+
+ To cope with this req->out.ptr is supplied. This will be updated to
+ point at the same offset into the packet as before this call
+*/
+static void smbcli_req_grow_data(struct smbcli_request *req, uint_t new_size)
+{
+ int delta;
+
+ smbcli_req_grow_allocation(req, new_size);
+
+ delta = new_size - req->out.data_size;
+
+ req->out.size += delta;
+ req->out.data_size += delta;
+
+ /* set the BCC to the new data size */
+ SSVAL(req->out.vwv, VWV(req->out.wct), new_size);
+}
+
+
+/*
+ setup a chained reply in req->out with the given word count and
+ initial data buffer size.
+*/
+NTSTATUS smbcli_chained_request_setup(struct smbcli_request *req,
+ uint8_t command,
+ uint_t wct, size_t buflen)
+{
+ uint_t new_size = 1 + (wct*2) + 2 + buflen;
+
+ SSVAL(req->out.vwv, VWV(0), command);
+ SSVAL(req->out.vwv, VWV(1), req->out.size - NBT_HDR_SIZE);
+
+ smbcli_req_grow_allocation(req, req->out.data_size + new_size);
+
+ req->out.vwv = req->out.buffer + req->out.size + 1;
+ SCVAL(req->out.vwv, -1, wct);
+ SSVAL(req->out.vwv, VWV(wct), buflen);
+
+ req->out.size += new_size;
+ req->out.data_size += new_size;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ aadvance to the next chained reply in a request
+*/
+NTSTATUS smbcli_chained_advance(struct smbcli_request *req)
+{
+ uint8_t *buffer;
+
+ if (CVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ buffer = req->in.hdr + SVAL(req->in.vwv, VWV(1));
+
+ if (buffer + 3 > req->in.buffer + req->in.size) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ req->in.vwv = buffer + 1;
+ req->in.wct = CVAL(buffer, 0);
+ if (buffer + 3 + req->in.wct*2 > req->in.buffer + req->in.size) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ req->in.data = req->in.vwv + 2 + req->in.wct * 2;
+ req->in.data_size = SVAL(req->in.vwv, VWV(req->in.wct));
+
+ /* fix the bufinfo */
+ smb_setup_bufinfo(req);
+
+ if (buffer + 3 + req->in.wct*2 + req->in.data_size >
+ req->in.buffer + req->in.size) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ send a message
+*/
+bool smbcli_request_send(struct smbcli_request *req)
+{
+ if (IVAL(req->out.buffer, 0) == 0) {
+ _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+ }
+
+ smbcli_request_calculate_sign_mac(req);
+
+ smbcli_transport_send(req);
+
+ return true;
+}
+
+
+/*
+ receive a response to a packet
+*/
+bool smbcli_request_receive(struct smbcli_request *req)
+{
+ /* req can be NULL when a send has failed. This eliminates lots of NULL
+ checks in each module */
+ if (!req) return false;
+
+ /* keep receiving packets until this one is replied to */
+ while (req->state <= SMBCLI_REQUEST_RECV) {
+ if (event_loop_once(req->transport->socket->event.ctx) != 0) {
+ return false;
+ }
+ }
+
+ return req->state == SMBCLI_REQUEST_DONE;
+}
+
+
+/*
+ handle oplock break requests from the server - return true if the request was
+ an oplock break
+*/
+bool smbcli_handle_oplock_break(struct smbcli_transport *transport, uint_t len, const uint8_t *hdr, const uint8_t *vwv)
+{
+ /* we must be very fussy about what we consider an oplock break to avoid
+ matching readbraw replies */
+ if (len != MIN_SMB_SIZE + VWV(8) + NBT_HDR_SIZE ||
+ (CVAL(hdr, HDR_FLG) & FLAG_REPLY) ||
+ CVAL(hdr,HDR_COM) != SMBlockingX ||
+ SVAL(hdr, HDR_MID) != 0xFFFF ||
+ SVAL(vwv,VWV(6)) != 0 ||
+ SVAL(vwv,VWV(7)) != 0) {
+ return false;
+ }
+
+ if (transport->oplock.handler) {
+ uint16_t tid = SVAL(hdr, HDR_TID);
+ uint16_t fnum = SVAL(vwv,VWV(2));
+ uint8_t level = CVAL(vwv,VWV(3)+1);
+ transport->oplock.handler(transport, tid, fnum, level, transport->oplock.private_data);
+ }
+
+ return true;
+}
+
+/*
+ wait for a reply to be received for a packet that just returns an error
+ code and nothing more
+*/
+_PUBLIC_ NTSTATUS smbcli_request_simple_recv(struct smbcli_request *req)
+{
+ (void) smbcli_request_receive(req);
+ return smbcli_request_destroy(req);
+}
+
+
+/* Return true if the last packet was in error */
+bool smbcli_request_is_error(struct smbcli_request *req)
+{
+ return NT_STATUS_IS_ERR(req->status);
+}
+
+/*
+ append a string into the data portion of the request packet
+
+ return the number of bytes added to the packet
+*/
+size_t smbcli_req_append_string(struct smbcli_request *req, const char *str, uint_t flags)
+{
+ size_t len;
+
+ /* determine string type to use */
+ if (!(flags & (STR_ASCII|STR_UNICODE))) {
+ flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
+ }
+
+ len = (strlen(str)+2) * MAX_BYTES_PER_CHAR;
+
+ smbcli_req_grow_allocation(req, len + req->out.data_size);
+
+ len = push_string(req->out.data + req->out.data_size, str, len, flags);
+
+ smbcli_req_grow_data(req, len + req->out.data_size);
+
+ return len;
+}
+
+
+/*
+ this is like smbcli_req_append_string but it also return the
+ non-terminated string byte length, which can be less than the number
+ of bytes consumed in the packet for 2 reasons:
+
+ 1) the string in the packet may be null terminated
+ 2) the string in the packet may need a 1 byte UCS2 alignment
+
+ this is used in places where the non-terminated string byte length is
+ placed in the packet as a separate field
+*/
+size_t smbcli_req_append_string_len(struct smbcli_request *req, const char *str, uint_t flags, int *len)
+{
+ int diff = 0;
+ size_t ret;
+
+ /* determine string type to use */
+ if (!(flags & (STR_ASCII|STR_UNICODE))) {
+ flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
+ }
+
+ /* see if an alignment byte will be used */
+ if ((flags & STR_UNICODE) && !(flags & STR_NOALIGN)) {
+ diff = ucs2_align(NULL, req->out.data + req->out.data_size, flags);
+ }
+
+ /* do the hard work */
+ ret = smbcli_req_append_string(req, str, flags);
+
+ /* see if we need to subtract the termination */
+ if (flags & STR_TERMINATE) {
+ diff += (flags & STR_UNICODE) ? 2 : 1;
+ }
+
+ if (ret >= diff) {
+ (*len) = ret - diff;
+ } else {
+ (*len) = ret;
+ }
+
+ return ret;
+}
+
+
+/*
+ push a string into the data portion of the request packet, growing it if necessary
+ this gets quite tricky - please be very careful to cover all cases when modifying this
+
+ if dest is NULL, then put the string at the end of the data portion of the packet
+
+ if dest_len is -1 then no limit applies
+*/
+size_t smbcli_req_append_ascii4(struct smbcli_request *req, const char *str, uint_t flags)
+{
+ size_t size;
+ smbcli_req_append_bytes(req, (const uint8_t *)"\4", 1);
+ size = smbcli_req_append_string(req, str, flags);
+ return size + 1;
+}
+
+
+/*
+ push a blob into the data portion of the request packet, growing it if necessary
+ this gets quite tricky - please be very careful to cover all cases when modifying this
+
+ if dest is NULL, then put the blob at the end of the data portion of the packet
+*/
+size_t smbcli_req_append_blob(struct smbcli_request *req, const DATA_BLOB *blob)
+{
+ smbcli_req_grow_allocation(req, req->out.data_size + blob->length);
+ memcpy(req->out.data + req->out.data_size, blob->data, blob->length);
+ smbcli_req_grow_data(req, req->out.data_size + blob->length);
+ return blob->length;
+}
+
+/*
+ append raw bytes into the data portion of the request packet
+ return the number of bytes added
+*/
+size_t smbcli_req_append_bytes(struct smbcli_request *req, const uint8_t *bytes, size_t byte_len)
+{
+ smbcli_req_grow_allocation(req, byte_len + req->out.data_size);
+ memcpy(req->out.data + req->out.data_size, bytes, byte_len);
+ smbcli_req_grow_data(req, byte_len + req->out.data_size);
+ return byte_len;
+}
+
+/*
+ append variable block (type 5 buffer) into the data portion of the request packet
+ return the number of bytes added
+*/
+size_t smbcli_req_append_var_block(struct smbcli_request *req, const uint8_t *bytes, uint16_t byte_len)
+{
+ smbcli_req_grow_allocation(req, byte_len + 3 + req->out.data_size);
+ SCVAL(req->out.data + req->out.data_size, 0, 5);
+ SSVAL(req->out.data + req->out.data_size, 1, byte_len); /* add field length */
+ if (byte_len > 0) {
+ memcpy(req->out.data + req->out.data_size + 3, bytes, byte_len);
+ }
+ smbcli_req_grow_data(req, byte_len + 3 + req->out.data_size);
+ return byte_len + 3;
+}
+
+
+/*
+ pull a UCS2 string from a request packet, returning a talloced unix string
+
+ the string length is limited by the 3 things:
+ - the data size in the request (end of packet)
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the packet
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the packet is returned
+*/
+static size_t smbcli_req_pull_ucs2(struct request_bufinfo *bufinfo, TALLOC_CTX *mem_ctx,
+ char **dest, const uint8_t *src, int byte_len, uint_t flags)
+{
+ int src_len, src_len2, alignment=0;
+ bool ret;
+ size_t ret_size;
+
+ if (!(flags & STR_NOALIGN) && ucs2_align(bufinfo->align_base, src, flags)) {
+ src++;
+ alignment=1;
+ if (byte_len != -1) {
+ byte_len--;
+ }
+ }
+
+ src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data);
+ if (src_len < 0) {
+ *dest = NULL;
+ return 0;
+ }
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+
+ src_len2 = utf16_len_n(src, src_len);
+
+ /* ucs2 strings must be at least 2 bytes long */
+ if (src_len2 < 2) {
+ *dest = NULL;
+ return 0;
+ }
+
+ ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, src, src_len2, (void **)dest, &ret_size, false);
+ if (!ret) {
+ *dest = NULL;
+ return 0;
+ }
+
+ return src_len2 + alignment;
+}
+
+/*
+ pull a ascii string from a request packet, returning a talloced string
+
+ the string length is limited by the 3 things:
+ - the data size in the request (end of packet)
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the packet
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the packet is returned
+*/
+size_t smbcli_req_pull_ascii(struct request_bufinfo *bufinfo, TALLOC_CTX *mem_ctx,
+ char **dest, const uint8_t *src, int byte_len, uint_t flags)
+{
+ int src_len, src_len2;
+ bool ret;
+ size_t ret_size;
+
+ src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data);
+ if (src_len < 0) {
+ *dest = NULL;
+ return 0;
+ }
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+ src_len2 = strnlen((const char *)src, src_len);
+ if (src_len2 < src_len - 1) {
+ /* include the termination if we didn't reach the end of the packet */
+ src_len2++;
+ }
+
+ ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (void **)dest, &ret_size, false);
+
+ if (!ret) {
+ *dest = NULL;
+ return 0;
+ }
+
+ return ret_size;
+}
+
+/**
+ pull a string from a request packet, returning a talloced string
+
+ the string length is limited by the 3 things:
+ - the data size in the request (end of packet)
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the packet
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the packet is returned
+*/
+size_t smbcli_req_pull_string(struct request_bufinfo *bufinfo, TALLOC_CTX *mem_ctx,
+ char **dest, const uint8_t *src, int byte_len, uint_t flags)
+{
+ if (!(flags & STR_ASCII) &&
+ (((flags & STR_UNICODE) || (bufinfo->flags & BUFINFO_FLAG_UNICODE)))) {
+ return smbcli_req_pull_ucs2(bufinfo, mem_ctx, dest, src, byte_len, flags);
+ }
+
+ return smbcli_req_pull_ascii(bufinfo, mem_ctx, dest, src, byte_len, flags);
+}
+
+
+/**
+ pull a DATA_BLOB from a reply packet, returning a talloced blob
+ make sure we don't go past end of packet
+
+ if byte_len is -1 then limit the blob only by packet size
+*/
+DATA_BLOB smbcli_req_pull_blob(struct request_bufinfo *bufinfo, TALLOC_CTX *mem_ctx, const uint8_t *src, int byte_len)
+{
+ int src_len;
+
+ src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data);
+
+ if (src_len < 0) {
+ return data_blob(NULL, 0);
+ }
+
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+
+ return data_blob_talloc(mem_ctx, src, src_len);
+}
+
+/* check that a lump of data in a request is within the bounds of the data section of
+ the packet */
+static bool smbcli_req_data_oob(struct request_bufinfo *bufinfo, const uint8_t *ptr, uint32_t count)
+{
+ /* be careful with wraparound! */
+ if ((uintptr_t)ptr < (uintptr_t)bufinfo->data ||
+ (uintptr_t)ptr >= (uintptr_t)bufinfo->data + bufinfo->data_size ||
+ count > bufinfo->data_size ||
+ (uintptr_t)ptr + count > (uintptr_t)bufinfo->data + bufinfo->data_size) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ pull a lump of data from a request packet
+
+ return false if any part is outside the data portion of the packet
+*/
+bool smbcli_raw_pull_data(struct request_bufinfo *bufinfo, const uint8_t *src, int len, uint8_t *dest)
+{
+ if (len == 0) return true;
+
+ if (smbcli_req_data_oob(bufinfo, src, len)) {
+ return false;
+ }
+
+ memcpy(dest, src, len);
+ return true;
+}
+
+
+/*
+ put a NTTIME into a packet
+*/
+void smbcli_push_nttime(void *base, uint16_t offset, NTTIME t)
+{
+ SBVAL(base, offset, t);
+}
+
+/*
+ pull a NTTIME from a packet
+*/
+NTTIME smbcli_pull_nttime(void *base, uint16_t offset)
+{
+ NTTIME ret = BVAL(base, offset);
+ return ret;
+}
+
+/**
+ pull a UCS2 string from a blob, returning a talloced unix string
+
+ the string length is limited by the 3 things:
+ - the data size in the blob
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the packet
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the blob is returned
+*/
+size_t smbcli_blob_pull_ucs2(TALLOC_CTX* mem_ctx,
+ const DATA_BLOB *blob, const char **dest,
+ const uint8_t *src, int byte_len, uint_t flags)
+{
+ int src_len, src_len2, alignment=0;
+ size_t ret_size;
+ bool ret;
+ char *dest2;
+
+ if (src < blob->data ||
+ src >= (blob->data + blob->length)) {
+ *dest = NULL;
+ return 0;
+ }
+
+ src_len = blob->length - PTR_DIFF(src, blob->data);
+
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+
+ if (!(flags & STR_NOALIGN) && ucs2_align(blob->data, src, flags)) {
+ src++;
+ alignment=1;
+ src_len--;
+ }
+
+ if (src_len < 2) {
+ *dest = NULL;
+ return 0;
+ }
+
+ src_len2 = utf16_len_n(src, src_len);
+
+ ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, src, src_len2, (void **)&dest2, &ret_size, false);
+ if (!ret) {
+ *dest = NULL;
+ return 0;
+ }
+ *dest = dest2;
+
+ return src_len2 + alignment;
+}
+
+/**
+ pull a ascii string from a blob, returning a talloced string
+
+ the string length is limited by the 3 things:
+ - the data size in the blob
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the blob
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the blob is returned
+*/
+static size_t smbcli_blob_pull_ascii(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *blob, const char **dest,
+ const uint8_t *src, int byte_len, uint_t flags)
+{
+ int src_len, src_len2;
+ size_t ret_size;
+ bool ret;
+ char *dest2;
+
+ src_len = blob->length - PTR_DIFF(src, blob->data);
+ if (src_len < 0) {
+ *dest = NULL;
+ return 0;
+ }
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+ src_len2 = strnlen((const char *)src, src_len);
+
+ if (src_len2 < src_len - 1) {
+ /* include the termination if we didn't reach the end of the packet */
+ src_len2++;
+ }
+
+ ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (void **)&dest2, &ret_size, false);
+
+ if (!ret) {
+ *dest = NULL;
+ return 0;
+ }
+ *dest = dest2;
+
+ return ret_size;
+}
+
+/**
+ pull a string from a blob, returning a talloced struct smb_wire_string
+
+ the string length is limited by the 3 things:
+ - the data size in the blob
+ - length field on the wire
+ - the end of string (null termination)
+
+ if STR_LEN8BIT is set in the flags then assume the length field is
+ 8 bits, instead of 32
+
+ on failure zero is returned and dest->s is set to NULL, otherwise the number
+ of bytes consumed in the blob is returned
+*/
+size_t smbcli_blob_pull_string(struct smbcli_session *session,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *blob,
+ struct smb_wire_string *dest,
+ uint16_t len_offset, uint16_t str_offset,
+ uint_t flags)
+{
+ int extra;
+ dest->s = NULL;
+
+ if (!(flags & STR_ASCII)) {
+ /* this is here to cope with SMB2 calls using the SMB
+ parsers. SMB2 will pass smbcli_session==NULL, which forces
+ unicode on (as used by SMB2) */
+ if (session == NULL) {
+ flags |= STR_UNICODE;
+ } else if (session->transport->negotiate.capabilities & CAP_UNICODE) {
+ flags |= STR_UNICODE;
+ }
+ }
+
+ if (flags & STR_LEN8BIT) {
+ if (len_offset > blob->length-1) {
+ return 0;
+ }
+ dest->private_length = CVAL(blob->data, len_offset);
+ } else {
+ if (len_offset > blob->length-4) {
+ return 0;
+ }
+ dest->private_length = IVAL(blob->data, len_offset);
+ }
+ extra = 0;
+ dest->s = NULL;
+ if (!(flags & STR_ASCII) && (flags & STR_UNICODE)) {
+ int align = 0;
+ if ((str_offset&1) && !(flags & STR_NOALIGN)) {
+ align = 1;
+ }
+ if (flags & STR_LEN_NOTERM) {
+ extra = 2;
+ }
+ return align + extra + smbcli_blob_pull_ucs2(mem_ctx, blob, &dest->s,
+ blob->data+str_offset+align,
+ dest->private_length, flags);
+ }
+
+ if (flags & STR_LEN_NOTERM) {
+ extra = 1;
+ }
+
+ return extra + smbcli_blob_pull_ascii(mem_ctx, blob, &dest->s,
+ blob->data+str_offset, dest->private_length, flags);
+}
+
+/**
+ pull a string from a blob, returning a talloced char *
+
+ Currently only used by the UNIX search info level.
+
+ the string length is limited by 2 things:
+ - the data size in the blob
+ - the end of string (null termination)
+
+ on failure zero is returned and dest->s is set to NULL, otherwise the number
+ of bytes consumed in the blob is returned
+*/
+size_t smbcli_blob_pull_unix_string(struct smbcli_session *session,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ const char **dest,
+ uint16_t str_offset,
+ uint_t flags)
+{
+ int extra = 0;
+ *dest = NULL;
+
+ if (!(flags & STR_ASCII) &&
+ ((flags & STR_UNICODE) ||
+ (session->transport->negotiate.capabilities & CAP_UNICODE))) {
+ int align = 0;
+ if ((str_offset&1) && !(flags & STR_NOALIGN)) {
+ align = 1;
+ }
+ if (flags & STR_LEN_NOTERM) {
+ extra = 2;
+ }
+ return align + extra + smbcli_blob_pull_ucs2(mem_ctx, blob, dest,
+ blob->data+str_offset+align,
+ -1, flags);
+ }
+
+ if (flags & STR_LEN_NOTERM) {
+ extra = 1;
+ }
+
+ return extra + smbcli_blob_pull_ascii(mem_ctx, blob, dest,
+ blob->data+str_offset, -1, flags);
+}
+
+
+/*
+ append a string into a blob
+*/
+size_t smbcli_blob_append_string(struct smbcli_session *session,
+ TALLOC_CTX *mem_ctx, DATA_BLOB *blob,
+ const char *str, uint_t flags)
+{
+ size_t max_len;
+ int len;
+
+ if (!str) return 0;
+
+ /* determine string type to use */
+ if (!(flags & (STR_ASCII|STR_UNICODE))) {
+ flags |= (session->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
+ }
+
+ max_len = (strlen(str)+2) * MAX_BYTES_PER_CHAR;
+
+ blob->data = talloc_realloc(mem_ctx, blob->data, uint8_t, blob->length + max_len);
+ if (!blob->data) {
+ return 0;
+ }
+
+ len = push_string(blob->data + blob->length, str, max_len, flags);
+
+ blob->length += len;
+
+ return len;
+}
+
+/*
+ pull a GUID structure from the wire. The buffer must be at least 16
+ bytes long
+ */
+enum ndr_err_code smbcli_pull_guid(void *base, uint16_t offset,
+ struct GUID *guid)
+{
+ DATA_BLOB blob;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ enum ndr_err_code ndr_err;
+
+ ZERO_STRUCTP(guid);
+
+ blob.data = offset + (uint8_t *)base;
+ blob.length = 16;
+ ndr_err = ndr_pull_struct_blob(&blob, tmp_ctx, NULL, guid,
+ (ndr_pull_flags_fn_t)ndr_pull_GUID);
+ talloc_free(tmp_ctx);
+ return ndr_err;
+}
+
+/*
+ push a guid onto the wire. The buffer must hold 16 bytes
+ */
+enum ndr_err_code smbcli_push_guid(void *base, uint16_t offset,
+ const struct GUID *guid)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, NULL,
+ guid, (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err) || blob.length != 16) {
+ talloc_free(tmp_ctx);
+ return ndr_err;
+ }
+ memcpy(offset + (uint8_t *)base, blob.data, blob.length);
+ talloc_free(tmp_ctx);
+ return ndr_err;
+}
diff --git a/source4/libcli/raw/rawsearch.c b/source4/libcli/raw/rawsearch.c
new file mode 100644
index 0000000000..9c90d45492
--- /dev/null
+++ b/source4/libcli/raw/rawsearch.c
@@ -0,0 +1,841 @@
+/*
+ Unix SMB/CIFS implementation.
+ client directory search routines
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+ Copyright (C) James Peach 2007
+
+ 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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+/****************************************************************************
+ Old style search backend - process output.
+****************************************************************************/
+static void smb_raw_search_backend(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ uint16_t count,
+ void *private_data,
+ smbcli_search_callback callback)
+
+{
+ union smb_search_data search_data;
+ int i;
+ uint8_t *p;
+
+ if (req->in.data_size < 3 + count*43) {
+ req->status = NT_STATUS_INVALID_PARAMETER;
+ return;
+ }
+
+ p = req->in.data + 3;
+
+ for (i=0; i < count; i++) {
+ char *name;
+
+ search_data.search.id.reserved = CVAL(p, 0);
+ memcpy(search_data.search.id.name, p+1, 11);
+ search_data.search.id.handle = CVAL(p, 12);
+ search_data.search.id.server_cookie = IVAL(p, 13);
+ search_data.search.id.client_cookie = IVAL(p, 17);
+ search_data.search.attrib = CVAL(p, 21);
+ search_data.search.write_time = raw_pull_dos_date(req->transport,
+ p + 22);
+ search_data.search.size = IVAL(p, 26);
+ smbcli_req_pull_ascii(&req->in.bufinfo, mem_ctx, &name, p+30, 13, STR_ASCII);
+ search_data.search.name = name;
+ if (!callback(private_data, &search_data)) {
+ break;
+ }
+ p += 43;
+ }
+}
+
+/****************************************************************************
+ Old style search first.
+****************************************************************************/
+static NTSTATUS smb_raw_search_first_old(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_first *io, void *private_data,
+ smbcli_search_callback callback)
+
+{
+ struct smbcli_request *req;
+ uint8_t op = SMBsearch;
+
+ if (io->generic.level == RAW_SEARCH_FFIRST) {
+ op = SMBffirst;
+ } else if (io->generic.level == RAW_SEARCH_FUNIQUE) {
+ op = SMBfunique;
+ }
+
+ req = smbcli_request_setup(tree, op, 2, 0);
+ if (!req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(req->out.vwv, VWV(0), io->search_first.in.max_count);
+ SSVAL(req->out.vwv, VWV(1), io->search_first.in.search_attrib);
+ smbcli_req_append_ascii4(req, io->search_first.in.pattern, STR_TERMINATE);
+ smbcli_req_append_var_block(req, NULL, 0);
+
+ if (!smbcli_request_send(req) ||
+ !smbcli_request_receive(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ if (NT_STATUS_IS_OK(req->status)) {
+ io->search_first.out.count = SVAL(req->in.vwv, VWV(0));
+ smb_raw_search_backend(req, mem_ctx, io->search_first.out.count, private_data, callback);
+ }
+
+ return smbcli_request_destroy(req);
+}
+
+/****************************************************************************
+ Old style search next.
+****************************************************************************/
+static NTSTATUS smb_raw_search_next_old(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_next *io, void *private_data,
+ smbcli_search_callback callback)
+
+{
+ struct smbcli_request *req;
+ uint8_t var_block[21];
+ uint8_t op = SMBsearch;
+
+ if (io->generic.level == RAW_SEARCH_FFIRST) {
+ op = SMBffirst;
+ }
+
+ req = smbcli_request_setup(tree, op, 2, 0);
+ if (!req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(req->out.vwv, VWV(0), io->search_next.in.max_count);
+ SSVAL(req->out.vwv, VWV(1), io->search_next.in.search_attrib);
+ smbcli_req_append_ascii4(req, "", STR_TERMINATE);
+
+ SCVAL(var_block, 0, io->search_next.in.id.reserved);
+ memcpy(&var_block[1], io->search_next.in.id.name, 11);
+ SCVAL(var_block, 12, io->search_next.in.id.handle);
+ SIVAL(var_block, 13, io->search_next.in.id.server_cookie);
+ SIVAL(var_block, 17, io->search_next.in.id.client_cookie);
+
+ smbcli_req_append_var_block(req, var_block, 21);
+
+ if (!smbcli_request_send(req) ||
+ !smbcli_request_receive(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ if (NT_STATUS_IS_OK(req->status)) {
+ io->search_next.out.count = SVAL(req->in.vwv, VWV(0));
+ smb_raw_search_backend(req, mem_ctx, io->search_next.out.count, private_data, callback);
+ }
+
+ return smbcli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Old style search next.
+****************************************************************************/
+static NTSTATUS smb_raw_search_close_old(struct smbcli_tree *tree,
+ union smb_search_close *io)
+{
+ struct smbcli_request *req;
+ uint8_t var_block[21];
+
+ req = smbcli_request_setup(tree, SMBfclose, 2, 0);
+ if (!req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(req->out.vwv, VWV(0), io->fclose.in.max_count);
+ SSVAL(req->out.vwv, VWV(1), io->fclose.in.search_attrib);
+ smbcli_req_append_ascii4(req, "", STR_TERMINATE);
+
+ SCVAL(var_block, 0, io->fclose.in.id.reserved);
+ memcpy(&var_block[1], io->fclose.in.id.name, 11);
+ SCVAL(var_block, 12, io->fclose.in.id.handle);
+ SIVAL(var_block, 13, io->fclose.in.id.server_cookie);
+ SIVAL(var_block, 17, io->fclose.in.id.client_cookie);
+
+ smbcli_req_append_var_block(req, var_block, 21);
+
+ if (!smbcli_request_send(req) ||
+ !smbcli_request_receive(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ return smbcli_request_destroy(req);
+}
+
+
+
+/****************************************************************************
+ Very raw search first - returns param/data blobs.
+****************************************************************************/
+static NTSTATUS smb_raw_search_first_blob(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx, /* used to allocate output blobs */
+ union smb_search_first *io,
+ DATA_BLOB *out_param_blob,
+ DATA_BLOB *out_data_blob)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_FINDFIRST;
+ NTSTATUS status;
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.data = data_blob(NULL, 0);
+ tp.in.max_param = 10;
+ tp.in.max_data = 0xFFFF;
+ tp.in.setup = &setup;
+
+ if (io->t2ffirst.level != RAW_SEARCH_TRANS2) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (io->t2ffirst.data_level >= RAW_SEARCH_DATA_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (io->t2ffirst.data_level == RAW_SEARCH_DATA_EA_LIST) {
+ if (!ea_push_name_list(mem_ctx,
+ &tp.in.data,
+ io->t2ffirst.in.num_names,
+ io->t2ffirst.in.ea_names)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
+ if (!tp.in.params.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(tp.in.params.data, 0, io->t2ffirst.in.search_attrib);
+ SSVAL(tp.in.params.data, 2, io->t2ffirst.in.max_count);
+ SSVAL(tp.in.params.data, 4, io->t2ffirst.in.flags);
+ SSVAL(tp.in.params.data, 6, io->t2ffirst.data_level);
+ SIVAL(tp.in.params.data, 8, io->t2ffirst.in.storage_type);
+
+ smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
+ io->t2ffirst.in.pattern, STR_TERMINATE);
+
+ status = smb_raw_trans2(tree, mem_ctx, &tp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ out_param_blob->length = tp.out.params.length;
+ out_param_blob->data = tp.out.params.data;
+ out_data_blob->length = tp.out.data.length;
+ out_data_blob->data = tp.out.data.data;
+
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Very raw search first - returns param/data blobs.
+ Used in CIFS-on-CIFS NTVFS.
+****************************************************************************/
+static NTSTATUS smb_raw_search_next_blob(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_next *io,
+ DATA_BLOB *out_param_blob,
+ DATA_BLOB *out_data_blob)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_FINDNEXT;
+ NTSTATUS status;
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.data = data_blob(NULL, 0);
+ tp.in.max_param = 10;
+ tp.in.max_data = 0xFFFF;
+ tp.in.setup = &setup;
+
+ if (io->t2fnext.level != RAW_SEARCH_TRANS2) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (io->t2fnext.data_level >= RAW_SEARCH_DATA_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (io->t2fnext.data_level == RAW_SEARCH_DATA_EA_LIST) {
+ if (!ea_push_name_list(mem_ctx,
+ &tp.in.data,
+ io->t2fnext.in.num_names,
+ io->t2fnext.in.ea_names)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
+ if (!tp.in.params.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(tp.in.params.data, 0, io->t2fnext.in.handle);
+ SSVAL(tp.in.params.data, 2, io->t2fnext.in.max_count);
+ SSVAL(tp.in.params.data, 4, io->t2fnext.data_level);
+ SIVAL(tp.in.params.data, 6, io->t2fnext.in.resume_key);
+ SSVAL(tp.in.params.data, 10, io->t2fnext.in.flags);
+
+ smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
+ io->t2fnext.in.last_name,
+ STR_TERMINATE);
+
+ status = smb_raw_trans2(tree, mem_ctx, &tp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ out_param_blob->length = tp.out.params.length;
+ out_param_blob->data = tp.out.params.data;
+ out_data_blob->length = tp.out.data.length;
+ out_data_blob->data = tp.out.data.data;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ parse the wire search formats that are in common between SMB and
+ SMB2
+*/
+NTSTATUS smb_raw_search_common(TALLOC_CTX *mem_ctx,
+ enum smb_search_data_level level,
+ const DATA_BLOB *blob,
+ union smb_search_data *data,
+ uint_t *next_ofs,
+ uint_t str_flags)
+{
+ uint_t len, blen;
+
+ if (blob->length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ *next_ofs = IVAL(blob->data, 0);
+ if (*next_ofs != 0) {
+ blen = *next_ofs;
+ } else {
+ blen = blob->length;
+ }
+
+ switch (level) {
+ case RAW_SEARCH_DATA_DIRECTORY_INFO:
+ if (blen < 65) return NT_STATUS_INFO_LENGTH_MISMATCH;
+ data->directory_info.file_index = IVAL(blob->data, 4);
+ data->directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
+ data->directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
+ data->directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
+ data->directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
+ data->directory_info.size = BVAL(blob->data, 40);
+ data->directory_info.alloc_size = BVAL(blob->data, 48);
+ data->directory_info.attrib = IVAL(blob->data, 56);
+ len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->directory_info.name,
+ 60, 64, str_flags);
+ if (*next_ofs != 0 && *next_ofs < 64+len) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
+ if (blen < 69) return NT_STATUS_INFO_LENGTH_MISMATCH;
+ data->full_directory_info.file_index = IVAL(blob->data, 4);
+ data->full_directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
+ data->full_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
+ data->full_directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
+ data->full_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
+ data->full_directory_info.size = BVAL(blob->data, 40);
+ data->full_directory_info.alloc_size = BVAL(blob->data, 48);
+ data->full_directory_info.attrib = IVAL(blob->data, 56);
+ data->full_directory_info.ea_size = IVAL(blob->data, 64);
+ len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->full_directory_info.name,
+ 60, 68, str_flags);
+ if (*next_ofs != 0 && *next_ofs < 68+len) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_NAME_INFO:
+ if (blen < 13) return NT_STATUS_INFO_LENGTH_MISMATCH;
+ data->name_info.file_index = IVAL(blob->data, 4);
+ len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->name_info.name,
+ 8, 12, str_flags);
+ if (*next_ofs != 0 && *next_ofs < 12+len) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ return NT_STATUS_OK;
+
+
+ case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
+ if (blen < 95) return NT_STATUS_INFO_LENGTH_MISMATCH;
+ data->both_directory_info.file_index = IVAL(blob->data, 4);
+ data->both_directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
+ data->both_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
+ data->both_directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
+ data->both_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
+ data->both_directory_info.size = BVAL(blob->data, 40);
+ data->both_directory_info.alloc_size = BVAL(blob->data, 48);
+ data->both_directory_info.attrib = IVAL(blob->data, 56);
+ data->both_directory_info.ea_size = IVAL(blob->data, 64);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->both_directory_info.short_name,
+ 68, 70, STR_LEN8BIT | STR_UNICODE);
+ len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->both_directory_info.name,
+ 60, 94, str_flags);
+ if (*next_ofs != 0 && *next_ofs < 94+len) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ return NT_STATUS_OK;
+
+
+ case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
+ if (blen < 81) return NT_STATUS_INFO_LENGTH_MISMATCH;
+ data->id_full_directory_info.file_index = IVAL(blob->data, 4);
+ data->id_full_directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
+ data->id_full_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
+ data->id_full_directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
+ data->id_full_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
+ data->id_full_directory_info.size = BVAL(blob->data, 40);
+ data->id_full_directory_info.alloc_size = BVAL(blob->data, 48);
+ data->id_full_directory_info.attrib = IVAL(blob->data, 56);
+ data->id_full_directory_info.ea_size = IVAL(blob->data, 64);
+ data->id_full_directory_info.file_id = BVAL(blob->data, 72);
+ len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->id_full_directory_info.name,
+ 60, 80, str_flags);
+ if (*next_ofs != 0 && *next_ofs < 80+len) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
+ if (blen < 105) return NT_STATUS_INFO_LENGTH_MISMATCH;
+ data->id_both_directory_info.file_index = IVAL(blob->data, 4);
+ data->id_both_directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
+ data->id_both_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
+ data->id_both_directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
+ data->id_both_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
+ data->id_both_directory_info.size = BVAL(blob->data, 40);
+ data->id_both_directory_info.alloc_size = BVAL(blob->data, 48);
+ data->id_both_directory_info.attrib = SVAL(blob->data, 56);
+ data->id_both_directory_info.ea_size = IVAL(blob->data, 64);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->id_both_directory_info.short_name,
+ 68, 70, STR_LEN8BIT | STR_UNICODE);
+ data->id_both_directory_info.file_id = BVAL(blob->data, 96);
+ len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->id_both_directory_info.name,
+ 60, 104, str_flags);
+ if (*next_ofs != 0 && *next_ofs < 104+len) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ return NT_STATUS_OK;
+
+ default:
+ break;
+ }
+
+ /* invalid level */
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+
+/*
+ parse a trans2 search response.
+ Return the number of bytes consumed
+ return 0 for success with end of list
+ return -1 for a parse error
+*/
+static int parse_trans2_search(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ enum smb_search_data_level level,
+ uint16_t flags,
+ DATA_BLOB *blob,
+ union smb_search_data *data)
+{
+ uint_t len, ofs;
+ uint32_t ea_size;
+ DATA_BLOB eablob;
+ NTSTATUS status;
+
+ switch (level) {
+ case RAW_SEARCH_DATA_GENERIC:
+ case RAW_SEARCH_DATA_SEARCH:
+ /* handled elsewhere */
+ return -1;
+
+ case RAW_SEARCH_DATA_STANDARD:
+ if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+ if (blob->length < 4) return -1;
+ data->standard.resume_key = IVAL(blob->data, 0);
+ blob->data += 4;
+ blob->length -= 4;
+ }
+ if (blob->length < 24) return -1;
+ data->standard.create_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 0);
+ data->standard.access_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 4);
+ data->standard.write_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 8);
+ data->standard.size = IVAL(blob->data, 12);
+ data->standard.alloc_size = IVAL(blob->data, 16);
+ data->standard.attrib = SVAL(blob->data, 20);
+ len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->standard.name,
+ 22, 23, STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM);
+ return len + 23;
+
+ case RAW_SEARCH_DATA_EA_SIZE:
+ if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+ if (blob->length < 4) return -1;
+ data->ea_size.resume_key = IVAL(blob->data, 0);
+ blob->data += 4;
+ blob->length -= 4;
+ }
+ if (blob->length < 28) return -1;
+ data->ea_size.create_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 0);
+ data->ea_size.access_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 4);
+ data->ea_size.write_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 8);
+ data->ea_size.size = IVAL(blob->data, 12);
+ data->ea_size.alloc_size = IVAL(blob->data, 16);
+ data->ea_size.attrib = SVAL(blob->data, 20);
+ data->ea_size.ea_size = IVAL(blob->data, 22);
+ len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->ea_size.name,
+ 26, 27, STR_LEN8BIT | STR_TERMINATE | STR_NOALIGN);
+ return len + 27 + 1;
+
+ case RAW_SEARCH_DATA_EA_LIST:
+ if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+ if (blob->length < 4) return -1;
+ data->ea_list.resume_key = IVAL(blob->data, 0);
+ blob->data += 4;
+ blob->length -= 4;
+ }
+ if (blob->length < 28) return -1;
+ data->ea_list.create_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 0);
+ data->ea_list.access_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 4);
+ data->ea_list.write_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 8);
+ data->ea_list.size = IVAL(blob->data, 12);
+ data->ea_list.alloc_size = IVAL(blob->data, 16);
+ data->ea_list.attrib = SVAL(blob->data, 20);
+ ea_size = IVAL(blob->data, 22);
+ if (ea_size > 0xFFFF) {
+ return -1;
+ }
+ eablob.data = blob->data + 22;
+ eablob.length = ea_size;
+ if (eablob.length > blob->length - 24) {
+ return -1;
+ }
+ status = ea_pull_list(&eablob, mem_ctx,
+ &data->ea_list.eas.num_eas,
+ &data->ea_list.eas.eas);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+ len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->ea_list.name,
+ 22+ea_size, 23+ea_size,
+ STR_LEN8BIT | STR_NOALIGN);
+ return len + ea_size + 23 + 1;
+
+ case RAW_SEARCH_DATA_UNIX_INFO:
+ if (blob->length < 109) return -1;
+ ofs = IVAL(blob->data, 0);
+ data->unix_info.file_index = IVAL(blob->data, 4);
+ data->unix_info.size = BVAL(blob->data, 8);
+ data->unix_info.alloc_size = BVAL(blob->data, 16);
+ data->unix_info.status_change_time = smbcli_pull_nttime(blob->data, 24);
+ data->unix_info.access_time = smbcli_pull_nttime(blob->data, 32);
+ data->unix_info.change_time = smbcli_pull_nttime(blob->data, 40);
+ data->unix_info.uid = IVAL(blob->data, 48);
+ data->unix_info.gid = IVAL(blob->data, 56);
+ data->unix_info.file_type = IVAL(blob->data, 64);
+ data->unix_info.dev_major = BVAL(blob->data, 68);
+ data->unix_info.dev_minor = BVAL(blob->data, 76);
+ data->unix_info.unique_id = BVAL(blob->data, 84);
+ data->unix_info.permissions = IVAL(blob->data, 92);
+ data->unix_info.nlink = IVAL(blob->data, 100);
+ /* There is no length field for this name but we know it's null terminated. */
+ len = smbcli_blob_pull_unix_string(tree->session, mem_ctx, blob,
+ &data->unix_info.name, 108, 0);
+ if (ofs != 0 && ofs < 108+len) {
+ return -1;
+ }
+ return ofs;
+
+ case RAW_SEARCH_DATA_UNIX_INFO2:
+ /* 8 - size of ofs + file_index
+ * 116 - size of unix_info2
+ * 4 - size of name length
+ * 2 - "." is the shortest name
+ */
+ if (blob->length < (116 + 8 + 4 + 2)) {
+ return -1;
+ }
+
+ ofs = IVAL(blob->data, 0);
+ data->unix_info2.file_index = IVAL(blob->data, 4);
+ data->unix_info2.end_of_file = BVAL(blob->data, 8);
+ data->unix_info2.num_bytes = BVAL(blob->data, 16);
+ data->unix_info2.status_change_time = smbcli_pull_nttime(blob->data, 24);
+ data->unix_info2.access_time = smbcli_pull_nttime(blob->data, 32);
+ data->unix_info2.change_time = smbcli_pull_nttime(blob->data, 40);
+ data->unix_info2.uid = IVAL(blob->data, 48);
+ data->unix_info2.gid = IVAL(blob->data, 56);
+ data->unix_info2.file_type = IVAL(blob->data, 64);
+ data->unix_info2.dev_major = BVAL(blob->data, 68);
+ data->unix_info2.dev_minor = BVAL(blob->data, 76);
+ data->unix_info2.unique_id = BVAL(blob->data, 84);
+ data->unix_info2.permissions = IVAL(blob->data, 92);
+ data->unix_info2.nlink = IVAL(blob->data, 100);
+ data->unix_info2.create_time = smbcli_pull_nttime(blob->data, 108);
+ data->unix_info2.file_flags = IVAL(blob->data, 116);
+ data->unix_info2.flags_mask = IVAL(blob->data, 120);
+
+ /* There is a 4 byte length field for this name. The length
+ * does not include the NULL terminator.
+ */
+ len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->unix_info2.name,
+ 8 + 116, /* offset to length */
+ 8 + 116 + 4, /* offset to string */
+ 0);
+
+ if (ofs != 0 && ofs < (8 + 116 + 4 + len)) {
+ return -1;
+ }
+
+ return ofs;
+
+ case RAW_SEARCH_DATA_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_NAME_INFO:
+ case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO: {
+ uint_t str_flags = STR_UNICODE;
+ if (!(tree->session->transport->negotiate.capabilities & CAP_UNICODE)) {
+ str_flags = STR_ASCII;
+ }
+
+ status = smb_raw_search_common(mem_ctx, level, blob, data, &ofs, str_flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+ return ofs;
+ }
+ }
+
+ /* invalid level */
+ return -1;
+}
+
+/****************************************************************************
+ Trans2 search backend - process output.
+****************************************************************************/
+static NTSTATUS smb_raw_t2search_backend(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ enum smb_search_data_level level,
+ uint16_t flags,
+ int16_t count,
+ DATA_BLOB *blob,
+ void *private_data,
+ smbcli_search_callback callback)
+
+{
+ int i;
+ DATA_BLOB blob2;
+
+ blob2.data = blob->data;
+ blob2.length = blob->length;
+
+ for (i=0; i < count; i++) {
+ union smb_search_data search_data;
+ uint_t len;
+
+ len = parse_trans2_search(tree, mem_ctx, level, flags, &blob2, &search_data);
+ if (len == -1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* the callback function can tell us that no more will
+ fit - in that case we stop, but it isn't an error */
+ if (!callback(private_data, &search_data)) {
+ break;
+ }
+
+ if (len == 0) break;
+
+ blob2.data += len;
+ blob2.length -= len;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/* Implements trans2findfirst2 and old search
+ */
+_PUBLIC_ NTSTATUS smb_raw_search_first(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_first *io, void *private_data,
+ smbcli_search_callback callback)
+{
+ DATA_BLOB p_blob, d_blob;
+ NTSTATUS status;
+
+ switch (io->generic.level) {
+ case RAW_SEARCH_SEARCH:
+ case RAW_SEARCH_FFIRST:
+ case RAW_SEARCH_FUNIQUE:
+ return smb_raw_search_first_old(tree, mem_ctx, io, private_data, callback);
+
+ case RAW_SEARCH_TRANS2:
+ break;
+
+ case RAW_SEARCH_SMB2:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ status = smb_raw_search_first_blob(tree, mem_ctx,
+ io, &p_blob, &d_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (p_blob.length < 10) {
+ DEBUG(1,("smb_raw_search_first: parms wrong size %d != expected_param_size\n",
+ (int)p_blob.length));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* process output data */
+ io->t2ffirst.out.handle = SVAL(p_blob.data, 0);
+ io->t2ffirst.out.count = SVAL(p_blob.data, 2);
+ io->t2ffirst.out.end_of_search = SVAL(p_blob.data, 4);
+
+ status = smb_raw_t2search_backend(tree, mem_ctx,
+ io->generic.data_level,
+ io->t2ffirst.in.flags, io->t2ffirst.out.count,
+ &d_blob, private_data, callback);
+
+ return status;
+}
+
+/* Implements trans2findnext2 and old smbsearch
+ */
+NTSTATUS smb_raw_search_next(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_next *io, void *private_data,
+ smbcli_search_callback callback)
+{
+ DATA_BLOB p_blob, d_blob;
+ NTSTATUS status;
+
+ switch (io->generic.level) {
+ case RAW_SEARCH_SEARCH:
+ case RAW_SEARCH_FFIRST:
+ return smb_raw_search_next_old(tree, mem_ctx, io, private_data, callback);
+
+ case RAW_SEARCH_FUNIQUE:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_SEARCH_TRANS2:
+ break;
+
+ case RAW_SEARCH_SMB2:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ status = smb_raw_search_next_blob(tree, mem_ctx,
+ io, &p_blob, &d_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (p_blob.length != 8) {
+ DEBUG(1,("smb_raw_search_next: parms wrong size %d != expected_param_size\n",
+ (int)p_blob.length));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* process output data */
+ io->t2fnext.out.count = SVAL(p_blob.data, 0);
+ io->t2fnext.out.end_of_search = SVAL(p_blob.data, 2);
+
+ status = smb_raw_t2search_backend(tree, mem_ctx,
+ io->generic.data_level,
+ io->t2fnext.in.flags, io->t2fnext.out.count,
+ &d_blob, private_data, callback);
+
+ return status;
+}
+
+/*
+ Implements trans2findclose2
+ */
+NTSTATUS smb_raw_search_close(struct smbcli_tree *tree,
+ union smb_search_close *io)
+{
+ struct smbcli_request *req;
+
+ if (io->generic.level == RAW_FINDCLOSE_FCLOSE) {
+ return smb_raw_search_close_old(tree, io);
+ }
+
+ req = smbcli_request_setup(tree, SMBfindclose, 1, 0);
+ if (!req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(req->out.vwv, VWV(0), io->findclose.in.handle);
+
+ if (smbcli_request_send(req)) {
+ (void) smbcli_request_receive(req);
+ }
+
+ return smbcli_request_destroy(req);
+}
diff --git a/source4/libcli/raw/rawsetfileinfo.c b/source4/libcli/raw/rawsetfileinfo.c
new file mode 100644
index 0000000000..f7dfb933f1
--- /dev/null
+++ b/source4/libcli/raw/rawsetfileinfo.c
@@ -0,0 +1,492 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAW_SFILEINFO_* calls
+ Copyright (C) James Myers 2003
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James Peach 2007
+
+ 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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+
+/*
+ Handle setfileinfo/setpathinfo passthu constructions
+*/
+bool smb_raw_setfileinfo_passthru(TALLOC_CTX *mem_ctx,
+ enum smb_setfileinfo_level level,
+ union smb_setfileinfo *parms,
+ DATA_BLOB *blob)
+{
+ uint_t len;
+
+#define NEED_BLOB(n) do { \
+ *blob = data_blob_talloc(mem_ctx, NULL, n); \
+ if (blob->data == NULL && n != 0) return false; \
+ } while (0)
+
+ switch (level) {
+ case RAW_SFILEINFO_BASIC_INFORMATION:
+ NEED_BLOB(40);
+ smbcli_push_nttime(blob->data, 0, parms->basic_info.in.create_time);
+ smbcli_push_nttime(blob->data, 8, parms->basic_info.in.access_time);
+ smbcli_push_nttime(blob->data, 16, parms->basic_info.in.write_time);
+ smbcli_push_nttime(blob->data, 24, parms->basic_info.in.change_time);
+ SIVAL(blob->data, 32, parms->basic_info.in.attrib);
+ SIVAL(blob->data, 36, 0); /* padding */
+ return true;
+
+ case RAW_SFILEINFO_DISPOSITION_INFORMATION:
+ NEED_BLOB(4);
+ SIVAL(blob->data, 0, parms->disposition_info.in.delete_on_close);
+ return true;
+
+ case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+ NEED_BLOB(8);
+ SBVAL(blob->data, 0, parms->allocation_info.in.alloc_size);
+ return true;
+
+ case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ NEED_BLOB(8);
+ SBVAL(blob->data, 0, parms->end_of_file_info.in.size);
+ return true;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ NEED_BLOB(12);
+ SIVAL(blob->data, 0, parms->rename_information.in.overwrite);
+ SIVAL(blob->data, 4, parms->rename_information.in.root_fid);
+ len = smbcli_blob_append_string(NULL, mem_ctx, blob,
+ parms->rename_information.in.new_name,
+ STR_UNICODE|STR_TERMINATE);
+ SIVAL(blob->data, 8, len - 2);
+ return true;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+ NEED_BLOB(20);
+ SIVAL(blob->data, 0, parms->rename_information.in.overwrite);
+ SBVAL(blob->data, 8, parms->rename_information.in.root_fid);
+ len = smbcli_blob_append_string(NULL, mem_ctx, blob,
+ parms->rename_information.in.new_name,
+ STR_UNICODE|STR_TERMINATE);
+ SIVAL(blob->data, 16, len - 2);
+ return true;
+
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ NEED_BLOB(8);
+ SBVAL(blob->data, 0, parms->position_information.in.position);
+ return true;
+
+ case RAW_SFILEINFO_MODE_INFORMATION:
+ NEED_BLOB(4);
+ SIVAL(blob->data, 0, parms->mode_information.in.mode);
+ return true;
+
+ case RAW_FILEINFO_SEC_DESC: {
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(blob, mem_ctx, NULL,
+ parms->set_secdesc.in.sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ case RAW_SFILEINFO_FULL_EA_INFORMATION:
+ printf("num_eas=%d\n", parms->full_ea_information.in.eas.num_eas);
+ NEED_BLOB(ea_list_size_chained(
+ parms->full_ea_information.in.eas.num_eas,
+ parms->full_ea_information.in.eas.eas, 4));
+ ea_put_list_chained(blob->data,
+ parms->full_ea_information.in.eas.num_eas,
+ parms->full_ea_information.in.eas.eas, 4);
+ return true;
+
+ /* Unhandled levels */
+ case RAW_SFILEINFO_PIPE_INFORMATION:
+ case RAW_SFILEINFO_VALID_DATA_INFORMATION:
+ case RAW_SFILEINFO_SHORT_NAME_INFORMATION:
+ case RAW_SFILEINFO_1025:
+ case RAW_SFILEINFO_1027:
+ case RAW_SFILEINFO_1029:
+ case RAW_SFILEINFO_1030:
+ case RAW_SFILEINFO_1031:
+ case RAW_SFILEINFO_1032:
+ case RAW_SFILEINFO_1036:
+ case RAW_SFILEINFO_1041:
+ case RAW_SFILEINFO_1042:
+ case RAW_SFILEINFO_1043:
+ case RAW_SFILEINFO_1044:
+ break;
+
+ default:
+ DEBUG(0,("Unhandled setfileinfo passthru level %d\n", level));
+ return false;
+ }
+
+ return false;
+}
+
+/*
+ Handle setfileinfo/setpathinfo trans2 backend.
+*/
+static bool smb_raw_setinfo_backend(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_setfileinfo *parms,
+ DATA_BLOB *blob)
+{
+ switch (parms->generic.level) {
+ case RAW_SFILEINFO_GENERIC:
+ case RAW_SFILEINFO_SETATTR:
+ case RAW_SFILEINFO_SETATTRE:
+ case RAW_SFILEINFO_SEC_DESC:
+ /* not handled here */
+ return false;
+
+ case RAW_SFILEINFO_STANDARD:
+ NEED_BLOB(12);
+ raw_push_dos_date2(tree->session->transport,
+ blob->data, 0, parms->standard.in.create_time);
+ raw_push_dos_date2(tree->session->transport,
+ blob->data, 4, parms->standard.in.access_time);
+ raw_push_dos_date2(tree->session->transport,
+ blob->data, 8, parms->standard.in.write_time);
+ return true;
+
+ case RAW_SFILEINFO_EA_SET:
+ NEED_BLOB(ea_list_size(parms->ea_set.in.num_eas, parms->ea_set.in.eas));
+ ea_put_list(blob->data, parms->ea_set.in.num_eas, parms->ea_set.in.eas);
+ return true;
+
+ case RAW_SFILEINFO_BASIC_INFO:
+ case RAW_SFILEINFO_BASIC_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_BASIC_INFORMATION,
+ parms, blob);
+
+ case RAW_SFILEINFO_UNIX_BASIC:
+ NEED_BLOB(100);
+ SBVAL(blob->data, 0, parms->unix_basic.in.end_of_file);
+ SBVAL(blob->data, 8, parms->unix_basic.in.num_bytes);
+ smbcli_push_nttime(blob->data, 16, parms->unix_basic.in.status_change_time);
+ smbcli_push_nttime(blob->data, 24, parms->unix_basic.in.access_time);
+ smbcli_push_nttime(blob->data, 32, parms->unix_basic.in.change_time);
+ SBVAL(blob->data, 40, parms->unix_basic.in.uid);
+ SBVAL(blob->data, 48, parms->unix_basic.in.gid);
+ SIVAL(blob->data, 56, parms->unix_basic.in.file_type);
+ SBVAL(blob->data, 60, parms->unix_basic.in.dev_major);
+ SBVAL(blob->data, 68, parms->unix_basic.in.dev_minor);
+ SBVAL(blob->data, 76, parms->unix_basic.in.unique_id);
+ SBVAL(blob->data, 84, parms->unix_basic.in.permissions);
+ SBVAL(blob->data, 92, parms->unix_basic.in.nlink);
+ return true;
+
+ case RAW_SFILEINFO_UNIX_INFO2:
+ NEED_BLOB(116);
+ SBVAL(blob->data, 0, parms->unix_info2.in.end_of_file);
+ SBVAL(blob->data, 8, parms->unix_info2.in.num_bytes);
+ smbcli_push_nttime(blob->data, 16, parms->unix_info2.in.status_change_time);
+ smbcli_push_nttime(blob->data, 24, parms->unix_info2.in.access_time);
+ smbcli_push_nttime(blob->data, 32, parms->unix_info2.in.change_time);
+ SBVAL(blob->data, 40,parms->unix_info2.in.uid);
+ SBVAL(blob->data, 48,parms->unix_info2.in.gid);
+ SIVAL(blob->data, 56,parms->unix_info2.in.file_type);
+ SBVAL(blob->data, 60,parms->unix_info2.in.dev_major);
+ SBVAL(blob->data, 68,parms->unix_info2.in.dev_minor);
+ SBVAL(blob->data, 76,parms->unix_info2.in.unique_id);
+ SBVAL(blob->data, 84,parms->unix_info2.in.permissions);
+ SBVAL(blob->data, 92,parms->unix_info2.in.nlink);
+ smbcli_push_nttime(blob->data, 100, parms->unix_info2.in.create_time);
+ SIVAL(blob->data, 108, parms->unix_info2.in.file_flags);
+ SIVAL(blob->data, 112, parms->unix_info2.in.flags_mask);
+ return true;
+
+ case RAW_SFILEINFO_DISPOSITION_INFO:
+ case RAW_SFILEINFO_DISPOSITION_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_DISPOSITION_INFORMATION,
+ parms, blob);
+
+ case RAW_SFILEINFO_ALLOCATION_INFO:
+ case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_ALLOCATION_INFORMATION,
+ parms, blob);
+
+ case RAW_SFILEINFO_END_OF_FILE_INFO:
+ case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_END_OF_FILE_INFORMATION,
+ parms, blob);
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_RENAME_INFORMATION,
+ parms, blob);
+
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_POSITION_INFORMATION,
+ parms, blob);
+
+ case RAW_SFILEINFO_MODE_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_MODE_INFORMATION,
+ parms, blob);
+
+ /* Unhandled passthru levels */
+ case RAW_SFILEINFO_PIPE_INFORMATION:
+ case RAW_SFILEINFO_VALID_DATA_INFORMATION:
+ case RAW_SFILEINFO_SHORT_NAME_INFORMATION:
+ case RAW_SFILEINFO_FULL_EA_INFORMATION:
+ case RAW_SFILEINFO_1025:
+ case RAW_SFILEINFO_1027:
+ case RAW_SFILEINFO_1029:
+ case RAW_SFILEINFO_1030:
+ case RAW_SFILEINFO_1031:
+ case RAW_SFILEINFO_1032:
+ case RAW_SFILEINFO_1036:
+ case RAW_SFILEINFO_1041:
+ case RAW_SFILEINFO_1042:
+ case RAW_SFILEINFO_1043:
+ case RAW_SFILEINFO_1044:
+ return smb_raw_setfileinfo_passthru(mem_ctx, parms->generic.level,
+ parms, blob);
+
+ /* Unhandled levels */
+
+ case RAW_SFILEINFO_UNIX_LINK:
+ case RAW_SFILEINFO_UNIX_HLINK:
+ case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+ break;
+ }
+
+ return false;
+}
+
+/****************************************************************************
+ Very raw set file info - takes data blob (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_setfileinfo_blob_send(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ uint16_t fnum,
+ uint16_t info_level,
+ DATA_BLOB *blob)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_SETFILEINFO;
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.max_param = 2;
+ tp.in.max_data = 0;
+ tp.in.setup = &setup;
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 6);
+ if (!tp.in.params.data) {
+ return NULL;
+ }
+ SSVAL(tp.in.params.data, 0, fnum);
+ SSVAL(tp.in.params.data, 2, info_level);
+ SSVAL(tp.in.params.data, 4, 0); /* reserved */
+
+ tp.in.data = *blob;
+
+ return smb_raw_trans2_send(tree, &tp);
+}
+
+/****************************************************************************
+ Very raw set path info - takes data blob
+****************************************************************************/
+static struct smbcli_request *smb_raw_setpathinfo_blob_send(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ const char *fname,
+ uint16_t info_level,
+ DATA_BLOB *blob)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_SETPATHINFO;
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.max_param = 2;
+ tp.in.max_data = 0;
+ tp.in.setup = &setup;
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 6);
+ if (!tp.in.params.data) {
+ return NULL;
+ }
+ SSVAL(tp.in.params.data, 0, info_level);
+ SIVAL(tp.in.params.data, 2, 0);
+ smbcli_blob_append_string(tree->session, mem_ctx,
+ &tp.in.params,
+ fname, STR_TERMINATE);
+
+ tp.in.data = *blob;
+
+ return smb_raw_trans2_send(tree, &tp);
+}
+
+/****************************************************************************
+ Handle setattr (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_setattr_send(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBsetatr, 8, 0);
+ if (!req) return NULL;
+
+ SSVAL(req->out.vwv, VWV(0), parms->setattr.in.attrib);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(1), parms->setattr.in.write_time);
+ memset(req->out.vwv + VWV(3), 0, 10); /* reserved */
+ smbcli_req_append_ascii4(req, parms->setattr.in.file.path, STR_TERMINATE);
+ smbcli_req_append_ascii4(req, "", STR_TERMINATE);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Handle setattrE. (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_setattrE_send(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBsetattrE, 7, 0);
+ if (!req) return NULL;
+
+ SSVAL(req->out.vwv, VWV(0), parms->setattre.in.file.fnum);
+ raw_push_dos_date2(tree->session->transport,
+ req->out.vwv, VWV(1), parms->setattre.in.create_time);
+ raw_push_dos_date2(tree->session->transport,
+ req->out.vwv, VWV(3), parms->setattre.in.access_time);
+ raw_push_dos_date2(tree->session->transport,
+ req->out.vwv, VWV(5), parms->setattre.in.write_time);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Set file info (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_setfileinfo_send(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ DATA_BLOB blob;
+ TALLOC_CTX *mem_ctx;
+ struct smbcli_request *req;
+
+ if (parms->generic.level == RAW_SFILEINFO_SETATTRE) {
+ return smb_raw_setattrE_send(tree, parms);
+ }
+ if (parms->generic.level == RAW_SFILEINFO_SEC_DESC) {
+ return smb_raw_set_secdesc_send(tree, parms);
+ }
+ if (parms->generic.level >= RAW_SFILEINFO_GENERIC) {
+ return NULL;
+ }
+
+ mem_ctx = talloc_init("setpathinfo");
+ if (!mem_ctx) return NULL;
+
+ if (!smb_raw_setinfo_backend(tree, mem_ctx, parms, &blob)) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ /* send request and process the output */
+ req = smb_raw_setfileinfo_blob_send(tree,
+ mem_ctx,
+ parms->generic.in.file.fnum,
+ parms->generic.level,
+ &blob);
+
+ talloc_free(mem_ctx);
+ return req;
+}
+
+/****************************************************************************
+ Set file info (async send)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_setfileinfo(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ struct smbcli_request *req = smb_raw_setfileinfo_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Set path info (async send)
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_setpathinfo_send(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ DATA_BLOB blob;
+ TALLOC_CTX *mem_ctx;
+ struct smbcli_request *req;
+
+ if (parms->generic.level == RAW_SFILEINFO_SETATTR) {
+ return smb_raw_setattr_send(tree, parms);
+ }
+ if (parms->generic.level >= RAW_SFILEINFO_GENERIC) {
+ return NULL;
+ }
+
+ mem_ctx = talloc_init("setpathinfo");
+ if (!mem_ctx) return NULL;
+
+ if (!smb_raw_setinfo_backend(tree, mem_ctx, parms, &blob)) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ /* send request and process the output */
+ req = smb_raw_setpathinfo_blob_send(tree,
+ mem_ctx,
+ parms->generic.in.file.path,
+ parms->generic.level,
+ &blob);
+
+ talloc_free(mem_ctx);
+ return req;
+}
+
+/****************************************************************************
+ Set path info (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_setpathinfo(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ struct smbcli_request *req = smb_raw_setpathinfo_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
diff --git a/source4/libcli/raw/rawshadow.c b/source4/libcli/raw/rawshadow.c
new file mode 100644
index 0000000000..b318c3e025
--- /dev/null
+++ b/source4/libcli/raw/rawshadow.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ shadow copy file operations
+
+ Copyright (C) Andrew Tridgell 2007
+
+ 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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/raw/ioctl.h"
+
+/*
+ get shadow volume data
+*/
+_PUBLIC_ NTSTATUS smb_raw_shadow_data(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx, struct smb_shadow_copy *info)
+{
+ union smb_ioctl nt;
+ NTSTATUS status;
+ DATA_BLOB blob;
+ uint32_t dlength;
+ int i;
+ uint32_t ofs;
+
+ nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
+ nt.ntioctl.in.function = FSCTL_GET_SHADOW_COPY_DATA;
+ nt.ntioctl.in.file.fnum = info->in.file.fnum;
+ nt.ntioctl.in.fsctl = true;
+ nt.ntioctl.in.filter = 0;
+ nt.ntioctl.in.max_data = info->in.max_data;
+ nt.ntioctl.in.blob = data_blob(NULL, 0);
+
+ status = smb_raw_ioctl(tree, mem_ctx, &nt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ blob = nt.ntioctl.out.blob;
+
+ if (blob.length < 12) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ info->out.num_volumes = IVAL(blob.data, 0);
+ info->out.num_names = IVAL(blob.data, 4);
+ dlength = IVAL(blob.data, 8);
+ if (dlength > blob.length - 12) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ info->out.names = talloc_array(mem_ctx, const char *, info->out.num_names);
+ NT_STATUS_HAVE_NO_MEMORY(info->out.names);
+
+ ofs = 12;
+ for (i=0;i<info->out.num_names;i++) {
+ size_t len;
+ len = smbcli_blob_pull_ucs2(info->out.names,
+ &blob, &info->out.names[i],
+ blob.data+ofs, -1, STR_TERMINATE);
+ if (len == 0) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ ofs += len;
+ }
+
+ return status;
+}
diff --git a/source4/libcli/raw/rawtrans.c b/source4/libcli/raw/rawtrans.c
new file mode 100644
index 0000000000..d68df56705
--- /dev/null
+++ b/source4/libcli/raw/rawtrans.c
@@ -0,0 +1,961 @@
+/*
+ Unix SMB/CIFS implementation.
+ raw trans/trans2/nttrans operations
+
+ Copyright (C) James Myers 2003 <myersjj@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 "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+#define TORTURE_TRANS_DATA 0
+
+/*
+ check out of bounds for incoming data
+*/
+static bool raw_trans_oob(struct smbcli_request *req,
+ uint_t offset, uint_t count)
+{
+ uint8_t *ptr;
+
+ if (count == 0) {
+ return false;
+ }
+
+ ptr = req->in.hdr + offset;
+
+ /* be careful with wraparound! */
+ if ((uintptr_t)ptr < (uintptr_t)req->in.data ||
+ (uintptr_t)ptr >= (uintptr_t)req->in.data + req->in.data_size ||
+ count > req->in.data_size ||
+ (uintptr_t)ptr + count > (uintptr_t)req->in.data + req->in.data_size) {
+ return true;
+ }
+ return false;
+}
+
+static size_t raw_trans_space_left(struct smbcli_request *req)
+{
+ if (req->transport->negotiate.max_xmit <= req->out.size) {
+ return 0;
+ }
+
+ return req->transport->negotiate.max_xmit - req->out.size;
+}
+
+struct smb_raw_trans2_recv_state {
+ uint8_t command;
+ uint32_t params_total;
+ uint32_t data_total;
+ uint32_t params_left;
+ uint32_t data_left;
+ bool got_first;
+ uint32_t recvd_data;
+ uint32_t recvd_param;
+ struct smb_trans2 io;
+};
+
+NTSTATUS smb_raw_trans2_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ struct smb_raw_trans2_recv_state *state;
+
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ state = talloc_get_type(req->recv_helper.private_data,
+ struct smb_raw_trans2_recv_state);
+
+ parms->out = state->io.out;
+ talloc_steal(mem_ctx, parms->out.setup);
+ talloc_steal(mem_ctx, parms->out.params.data);
+ talloc_steal(mem_ctx, parms->out.data.data);
+ talloc_free(state);
+
+ ZERO_STRUCT(req->recv_helper);
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+static enum smbcli_request_state smb_raw_trans2_ship_rest(struct smbcli_request *req,
+ struct smb_raw_trans2_recv_state *state);
+
+/*
+ * This helper returns SMBCLI_REQUEST_RECV until all data has arrived
+ */
+static enum smbcli_request_state smb_raw_trans2_recv_helper(struct smbcli_request *req)
+{
+ struct smb_raw_trans2_recv_state *state = talloc_get_type(req->recv_helper.private_data,
+ struct smb_raw_trans2_recv_state);
+ uint16_t param_count, param_ofs, param_disp;
+ uint16_t data_count, data_ofs, data_disp;
+ uint16_t total_data, total_param;
+ uint8_t setup_count;
+
+ /*
+ * An NT RPC pipe call can return ERRDOS, ERRmoredata
+ * to a trans call. This is not an error and should not
+ * be treated as such.
+ */
+ if (smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ if (state->params_left > 0 || state->data_left > 0) {
+ return smb_raw_trans2_ship_rest(req, state);
+ }
+
+ SMBCLI_CHECK_MIN_WCT(req, 10);
+
+ total_data = SVAL(req->in.vwv, VWV(1));
+ total_param = SVAL(req->in.vwv, VWV(0));
+ setup_count = CVAL(req->in.vwv, VWV(9));
+
+ param_count = SVAL(req->in.vwv, VWV(3));
+ param_ofs = SVAL(req->in.vwv, VWV(4));
+ param_disp = SVAL(req->in.vwv, VWV(5));
+
+ data_count = SVAL(req->in.vwv, VWV(6));
+ data_ofs = SVAL(req->in.vwv, VWV(7));
+ data_disp = SVAL(req->in.vwv, VWV(8));
+
+ if (!state->got_first) {
+ if (total_param > 0) {
+ state->io.out.params = data_blob_talloc(state, NULL, total_param);
+ if (!state->io.out.params.data) {
+ goto nomem;
+ }
+ }
+
+ if (total_data > 0) {
+ state->io.out.data = data_blob_talloc(state, NULL, total_data);
+ if (!state->io.out.data.data) {
+ goto nomem;
+ }
+ }
+
+ if (setup_count > 0) {
+ uint16_t i;
+
+ SMBCLI_CHECK_WCT(req, 10 + setup_count);
+
+ state->io.out.setup_count = setup_count;
+ state->io.out.setup = talloc_array(state, uint16_t, setup_count);
+ if (!state->io.out.setup) {
+ goto nomem;
+ }
+ for (i=0; i < setup_count; i++) {
+ state->io.out.setup[i] = SVAL(req->in.vwv, VWV(10+i));
+ }
+ }
+
+ state->got_first = true;
+ }
+
+ if (total_data > state->io.out.data.length ||
+ total_param > state->io.out.params.length) {
+ /* they must *only* shrink */
+ DEBUG(1,("smb_raw_trans2_recv_helper: data/params expanded!\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+
+ state->io.out.data.length = total_data;
+ state->io.out.params.length = total_param;
+
+ if (data_count + data_disp > total_data ||
+ param_count + param_disp > total_param) {
+ DEBUG(1,("smb_raw_trans2_recv_helper: Buffer overflow\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+
+ /* check the server isn't being nasty */
+ if (raw_trans_oob(req, param_ofs, param_count) ||
+ raw_trans_oob(req, data_ofs, data_count)) {
+ DEBUG(1,("smb_raw_trans2_recv_helper: out of bounds parameters!\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+
+ if (data_count) {
+ memcpy(state->io.out.data.data + data_disp,
+ req->in.hdr + data_ofs,
+ data_count);
+ }
+
+ if (param_count) {
+ memcpy(state->io.out.params.data + param_disp,
+ req->in.hdr + param_ofs,
+ param_count);
+ }
+
+ state->recvd_param += param_count;
+ state->recvd_data += data_count;
+
+ if (state->recvd_data < total_data ||
+ state->recvd_param < total_param) {
+
+ /* we don't need the in buffer any more */
+ talloc_free(req->in.buffer);
+ ZERO_STRUCT(req->in);
+
+ /* we still wait for more data */
+ DEBUG(10,("smb_raw_trans2_recv_helper: more data needed\n"));
+ return SMBCLI_REQUEST_RECV;
+ }
+
+ DEBUG(10,("smb_raw_trans2_recv_helper: done\n"));
+ return SMBCLI_REQUEST_DONE;
+
+nomem:
+ req->status = NT_STATUS_NO_MEMORY;
+failed:
+ return SMBCLI_REQUEST_ERROR;
+}
+
+_PUBLIC_ NTSTATUS smb_raw_trans_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ return smb_raw_trans2_recv(req, mem_ctx, parms);
+}
+
+
+/*
+ trans/trans2 raw async interface - only BLOBs used in this interface.
+*/
+struct smbcli_request *smb_raw_trans_send_backend(struct smbcli_tree *tree,
+ struct smb_trans2 *parms,
+ uint8_t command)
+{
+ struct smb_raw_trans2_recv_state *state;
+ struct smbcli_request *req;
+ int i;
+ int padding;
+ size_t space_left;
+ size_t namelen = 0;
+ DATA_BLOB params_chunk;
+ uint16_t ofs;
+ uint16_t params_ofs = 0;
+ DATA_BLOB data_chunk;
+ uint16_t data_ofs = 0;
+
+ if (parms->in.params.length > UINT16_MAX ||
+ parms->in.data.length > UINT16_MAX) {
+ DEBUG(3,("Attempt to send invalid trans2 request (params %u, data %u)\n",
+ (unsigned)parms->in.params.length, (unsigned)parms->in.data.length));
+ return NULL;
+ }
+
+
+ if (command == SMBtrans)
+ padding = 1;
+ else
+ padding = 3;
+
+ req = smbcli_request_setup(tree, command,
+ 14 + parms->in.setup_count,
+ padding);
+ if (!req) {
+ return NULL;
+ }
+
+ state = talloc_zero(req, struct smb_raw_trans2_recv_state);
+ if (!state) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ state->command = command;
+
+ /* make sure we don't leak data via the padding */
+ memset(req->out.data, 0, padding);
+
+ /* Watch out, this changes the req->out.* pointers */
+ if (command == SMBtrans && parms->in.trans_name) {
+ namelen = smbcli_req_append_string(req, parms->in.trans_name,
+ STR_TERMINATE);
+ }
+
+ ofs = PTR_DIFF(req->out.data,req->out.hdr)+padding+namelen;
+
+ /* see how much bytes of the params block we can ship in the first request */
+ space_left = raw_trans_space_left(req);
+
+ params_chunk.length = MIN(parms->in.params.length, space_left);
+ params_chunk.data = parms->in.params.data;
+ params_ofs = ofs;
+
+ state->params_left = parms->in.params.length - params_chunk.length;
+
+ if (state->params_left > 0) {
+ /* we copy the whole params block, if needed we can optimize that latter */
+ state->io.in.params = data_blob_talloc(state, NULL, parms->in.params.length);
+ if (!state->io.in.params.data) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+ memcpy(state->io.in.params.data,
+ parms->in.params.data,
+ parms->in.params.length);
+ }
+
+ /* see how much bytes of the data block we can ship in the first request */
+ space_left -= params_chunk.length;
+
+#if TORTURE_TRANS_DATA
+ if (space_left > 1) {
+ space_left /= 2;
+ }
+#endif
+
+ data_chunk.length = MIN(parms->in.data.length, space_left);
+ data_chunk.data = parms->in.data.data;
+ data_ofs = params_ofs + params_chunk.length;
+
+ state->data_left = parms->in.data.length - data_chunk.length;
+
+ if (state->data_left > 0) {
+ /* we copy the whole params block, if needed we can optimize that latter */
+ state->io.in.data = data_blob_talloc(state, NULL, parms->in.data.length);
+ if (!state->io.in.data.data) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+ memcpy(state->io.in.data.data,
+ parms->in.data.data,
+ parms->in.data.length);
+ }
+
+ state->params_total = parms->in.params.length;
+ state->data_total = parms->in.data.length;
+
+ /* primary request */
+ SSVAL(req->out.vwv,VWV(0),parms->in.params.length);
+ SSVAL(req->out.vwv,VWV(1),parms->in.data.length);
+ SSVAL(req->out.vwv,VWV(2),parms->in.max_param);
+ SSVAL(req->out.vwv,VWV(3),parms->in.max_data);
+ SCVAL(req->out.vwv,VWV(4),parms->in.max_setup);
+ SCVAL(req->out.vwv,VWV(4)+1,0); /* reserved */
+ SSVAL(req->out.vwv,VWV(5),parms->in.flags);
+ SIVAL(req->out.vwv,VWV(6),parms->in.timeout);
+ SSVAL(req->out.vwv,VWV(8),0); /* reserved */
+ SSVAL(req->out.vwv,VWV(9),params_chunk.length);
+ SSVAL(req->out.vwv,VWV(10),params_ofs);
+ SSVAL(req->out.vwv,VWV(11),data_chunk.length);
+ SSVAL(req->out.vwv,VWV(12),data_ofs);
+ SCVAL(req->out.vwv,VWV(13),parms->in.setup_count);
+ SCVAL(req->out.vwv,VWV(13)+1,0); /* reserved */
+ for (i=0;i<parms->in.setup_count;i++) {
+ SSVAL(req->out.vwv,VWV(14)+VWV(i),parms->in.setup[i]);
+ }
+ smbcli_req_append_blob(req, &params_chunk);
+ smbcli_req_append_blob(req, &data_chunk);
+
+ /* add the helper which will check that all multi-part replies are
+ in before an async client callack will be issued */
+ req->recv_helper.fn = smb_raw_trans2_recv_helper;
+ req->recv_helper.private_data = state;
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+static enum smbcli_request_state smb_raw_trans2_ship_next(struct smbcli_request *req,
+ struct smb_raw_trans2_recv_state *state)
+{
+ struct smbcli_request *req2;
+ size_t space_left;
+ DATA_BLOB params_chunk;
+ uint16_t ofs;
+ uint16_t params_ofs = 0;
+ uint16_t params_disp = 0;
+ DATA_BLOB data_chunk;
+ uint16_t data_ofs = 0;
+ uint16_t data_disp = 0;
+ uint8_t wct;
+
+ if (state->command == SMBtrans2) {
+ wct = 9;
+ } else {
+ wct = 8;
+ }
+
+ req2 = smbcli_request_setup(req->tree, state->command+1, wct, 0);
+ if (!req2) {
+ goto nomem;
+ }
+ req2->mid = req->mid;
+ SSVAL(req2->out.hdr, HDR_MID, req2->mid);
+
+ ofs = PTR_DIFF(req2->out.data,req2->out.hdr);
+
+ /* see how much bytes of the params block we can ship in the first request */
+ space_left = raw_trans_space_left(req2);
+
+ params_disp = state->io.in.params.length - state->params_left;
+ params_chunk.length = MIN(state->params_left, space_left);
+ params_chunk.data = state->io.in.params.data + params_disp;
+ params_ofs = ofs;
+
+ state->params_left -= params_chunk.length;
+
+ /* see how much bytes of the data block we can ship in the first request */
+ space_left -= params_chunk.length;
+
+#if TORTURE_TRANS_DATA
+ if (space_left > 1) {
+ space_left /= 2;
+ }
+#endif
+
+ data_disp = state->io.in.data.length - state->data_left;
+ data_chunk.length = MIN(state->data_left, space_left);
+ data_chunk.data = state->io.in.data.data + data_disp;
+ data_ofs = params_ofs+params_chunk.length;
+
+ state->data_left -= data_chunk.length;
+
+ SSVAL(req2->out.vwv,VWV(0), state->params_total);
+ SSVAL(req2->out.vwv,VWV(1), state->data_total);
+ SSVAL(req2->out.vwv,VWV(2), params_chunk.length);
+ SSVAL(req2->out.vwv,VWV(3), params_ofs);
+ SSVAL(req2->out.vwv,VWV(4), params_disp);
+ SSVAL(req2->out.vwv,VWV(5), data_chunk.length);
+ SSVAL(req2->out.vwv,VWV(6), data_ofs);
+ SSVAL(req2->out.vwv,VWV(7), data_disp);
+ if (wct == 9) {
+ SSVAL(req2->out.vwv,VWV(8), 0xFFFF);
+ }
+
+ smbcli_req_append_blob(req2, &params_chunk);
+ smbcli_req_append_blob(req2, &data_chunk);
+
+ /*
+ * it's a one way request but we need
+ * the seq_num, so we destroy req2 by hand
+ */
+ if (!smbcli_request_send(req2)) {
+ goto failed;
+ }
+
+ req->seq_num = req2->seq_num;
+ smbcli_request_destroy(req2);
+
+ return SMBCLI_REQUEST_RECV;
+
+nomem:
+ req->status = NT_STATUS_NO_MEMORY;
+failed:
+ if (req2) {
+ req->status = smbcli_request_destroy(req2);
+ }
+ return SMBCLI_REQUEST_ERROR;
+}
+
+static enum smbcli_request_state smb_raw_trans2_ship_rest(struct smbcli_request *req,
+ struct smb_raw_trans2_recv_state *state)
+{
+ enum smbcli_request_state ret = SMBCLI_REQUEST_ERROR;
+
+ while (state->params_left > 0 || state->data_left > 0) {
+ ret = smb_raw_trans2_ship_next(req, state);
+ if (ret != SMBCLI_REQUEST_RECV) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+/*
+ trans/trans2 raw async interface - only BLOBs used in this interface.
+ note that this doesn't yet support multi-part requests
+*/
+_PUBLIC_ struct smbcli_request *smb_raw_trans_send(struct smbcli_tree *tree,
+ struct smb_trans2 *parms)
+{
+ return smb_raw_trans_send_backend(tree, parms, SMBtrans);
+}
+
+struct smbcli_request *smb_raw_trans2_send(struct smbcli_tree *tree,
+ struct smb_trans2 *parms)
+{
+ return smb_raw_trans_send_backend(tree, parms, SMBtrans2);
+}
+
+/*
+ trans2 synchronous blob interface
+*/
+NTSTATUS smb_raw_trans2(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ struct smbcli_request *req;
+ req = smb_raw_trans2_send(tree, parms);
+ if (!req) return NT_STATUS_UNSUCCESSFUL;
+ return smb_raw_trans2_recv(req, mem_ctx, parms);
+}
+
+
+/*
+ trans synchronous blob interface
+*/
+_PUBLIC_ NTSTATUS smb_raw_trans(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ struct smbcli_request *req;
+ req = smb_raw_trans_send(tree, parms);
+ if (!req) return NT_STATUS_UNSUCCESSFUL;
+ return smb_raw_trans_recv(req, mem_ctx, parms);
+}
+
+struct smb_raw_nttrans_recv_state {
+ uint32_t params_total;
+ uint32_t data_total;
+ uint32_t params_left;
+ uint32_t data_left;
+ bool got_first;
+ uint32_t recvd_data;
+ uint32_t recvd_param;
+ struct smb_nttrans io;
+};
+
+NTSTATUS smb_raw_nttrans_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_nttrans *parms)
+{
+ struct smb_raw_nttrans_recv_state *state;
+
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ state = talloc_get_type(req->recv_helper.private_data,
+ struct smb_raw_nttrans_recv_state);
+
+ parms->out = state->io.out;
+ talloc_steal(mem_ctx, parms->out.setup);
+ talloc_steal(mem_ctx, parms->out.params.data);
+ talloc_steal(mem_ctx, parms->out.data.data);
+ talloc_free(state);
+
+ ZERO_STRUCT(req->recv_helper);
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+static enum smbcli_request_state smb_raw_nttrans_ship_rest(struct smbcli_request *req,
+ struct smb_raw_nttrans_recv_state *state);
+
+/*
+ * This helper returns SMBCLI_REQUEST_RECV until all data has arrived
+ */
+static enum smbcli_request_state smb_raw_nttrans_recv_helper(struct smbcli_request *req)
+{
+ struct smb_raw_nttrans_recv_state *state = talloc_get_type(req->recv_helper.private_data,
+ struct smb_raw_nttrans_recv_state);
+ uint32_t param_count, param_ofs, param_disp;
+ uint32_t data_count, data_ofs, data_disp;
+ uint32_t total_data, total_param;
+ uint8_t setup_count;
+
+ /*
+ * An NT RPC pipe call can return ERRDOS, ERRmoredata
+ * to a trans call. This is not an error and should not
+ * be treated as such.
+ */
+ if (smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ /* sanity check */
+ if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) {
+ DEBUG(0,("smb_raw_nttrans_recv_helper: Expected %s response, got command 0x%02x\n",
+ "SMBnttrans",
+ CVAL(req->in.hdr,HDR_COM)));
+ req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto failed;
+ }
+
+ if (state->params_left > 0 || state->data_left > 0) {
+ return smb_raw_nttrans_ship_rest(req, state);
+ }
+
+ /* this is the first packet of the response */
+ SMBCLI_CHECK_MIN_WCT(req, 18);
+
+ total_param = IVAL(req->in.vwv, 3);
+ total_data = IVAL(req->in.vwv, 7);
+ setup_count = CVAL(req->in.vwv, 35);
+
+ param_count = IVAL(req->in.vwv, 11);
+ param_ofs = IVAL(req->in.vwv, 15);
+ param_disp = IVAL(req->in.vwv, 19);
+
+ data_count = IVAL(req->in.vwv, 23);
+ data_ofs = IVAL(req->in.vwv, 27);
+ data_disp = IVAL(req->in.vwv, 31);
+
+ if (!state->got_first) {
+ if (total_param > 0) {
+ state->io.out.params = data_blob_talloc(state, NULL, total_param);
+ if (!state->io.out.params.data) {
+ goto nomem;
+ }
+ }
+
+ if (total_data > 0) {
+ state->io.out.data = data_blob_talloc(state, NULL, total_data);
+ if (!state->io.out.data.data) {
+ goto nomem;
+ }
+ }
+
+ if (setup_count > 0) {
+ SMBCLI_CHECK_WCT(req, 18 + setup_count);
+
+ state->io.out.setup_count = setup_count;
+ state->io.out.setup = talloc_array(state, uint8_t,
+ setup_count * VWV(1));
+ if (!state->io.out.setup) {
+ goto nomem;
+ }
+ memcpy(state->io.out.setup, (uint8_t *)req->out.vwv + VWV(18),
+ setup_count * VWV(1));
+ }
+
+ state->got_first = true;
+ }
+
+ if (total_data > state->io.out.data.length ||
+ total_param > state->io.out.params.length) {
+ /* they must *only* shrink */
+ DEBUG(1,("smb_raw_nttrans_recv_helper: data/params expanded!\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+
+ state->io.out.data.length = total_data;
+ state->io.out.params.length = total_param;
+
+ if (data_count + data_disp > total_data ||
+ param_count + param_disp > total_param) {
+ DEBUG(1,("smb_raw_nttrans_recv_helper: Buffer overflow\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+
+ /* check the server isn't being nasty */
+ if (raw_trans_oob(req, param_ofs, param_count) ||
+ raw_trans_oob(req, data_ofs, data_count)) {
+ DEBUG(1,("smb_raw_nttrans_recv_helper: out of bounds parameters!\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+
+ if (data_count) {
+ memcpy(state->io.out.data.data + data_disp,
+ req->in.hdr + data_ofs,
+ data_count);
+ }
+
+ if (param_count) {
+ memcpy(state->io.out.params.data + param_disp,
+ req->in.hdr + param_ofs,
+ param_count);
+ }
+
+ state->recvd_param += param_count;
+ state->recvd_data += data_count;
+
+ if (state->recvd_data < total_data ||
+ state->recvd_param < total_param) {
+
+ /* we don't need the in buffer any more */
+ talloc_free(req->in.buffer);
+ ZERO_STRUCT(req->in);
+
+ /* we still wait for more data */
+ DEBUG(10,("smb_raw_nttrans_recv_helper: more data needed\n"));
+ return SMBCLI_REQUEST_RECV;
+ }
+
+ DEBUG(10,("smb_raw_nttrans_recv_helper: done\n"));
+ return SMBCLI_REQUEST_DONE;
+
+nomem:
+ req->status = NT_STATUS_NO_MEMORY;
+failed:
+ return SMBCLI_REQUEST_ERROR;
+}
+
+/****************************************************************************
+ nttrans raw - only BLOBs used in this interface.
+ at the moment we only handle a single primary request
+****************************************************************************/
+struct smbcli_request *smb_raw_nttrans_send(struct smbcli_tree *tree,
+ struct smb_nttrans *parms)
+{
+ struct smbcli_request *req;
+ struct smb_raw_nttrans_recv_state *state;
+ uint32_t ofs;
+ size_t space_left;
+ DATA_BLOB params_chunk;
+ uint32_t params_ofs;
+ DATA_BLOB data_chunk;
+ uint32_t data_ofs;
+ int align = 0;
+
+ /* only align if there are parameters or data */
+ if (parms->in.params.length || parms->in.data.length) {
+ align = 3;
+ }
+
+ req = smbcli_request_setup(tree, SMBnttrans,
+ 19 + parms->in.setup_count, align);
+ if (!req) {
+ return NULL;
+ }
+
+ state = talloc_zero(req, struct smb_raw_nttrans_recv_state);
+ if (!state) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ /* fill in SMB parameters */
+
+ if (align != 0) {
+ memset(req->out.data, 0, align);
+ }
+
+ ofs = PTR_DIFF(req->out.data,req->out.hdr)+align;
+
+ /* see how much bytes of the params block we can ship in the first request */
+ space_left = raw_trans_space_left(req);
+
+ params_chunk.length = MIN(parms->in.params.length, space_left);
+ params_chunk.data = parms->in.params.data;
+ params_ofs = ofs;
+
+ state->params_left = parms->in.params.length - params_chunk.length;
+
+ if (state->params_left > 0) {
+ /* we copy the whole params block, if needed we can optimize that latter */
+ state->io.in.params = data_blob_talloc(state, NULL, parms->in.params.length);
+ if (!state->io.in.params.data) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+ memcpy(state->io.in.params.data,
+ parms->in.params.data,
+ parms->in.params.length);
+ }
+
+ /* see how much bytes of the data block we can ship in the first request */
+ space_left -= params_chunk.length;
+
+#if TORTURE_TRANS_DATA
+ if (space_left > 1) {
+ space_left /= 2;
+ }
+#endif
+
+ data_chunk.length = MIN(parms->in.data.length, space_left);
+ data_chunk.data = parms->in.data.data;
+ data_ofs = params_ofs + params_chunk.length;
+
+ state->data_left = parms->in.data.length - data_chunk.length;
+
+ if (state->data_left > 0) {
+ /* we copy the whole params block, if needed we can optimize that latter */
+ state->io.in.data = data_blob_talloc(state, NULL, parms->in.data.length);
+ if (!state->io.in.data.data) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+ memcpy(state->io.in.data.data,
+ parms->in.data.data,
+ parms->in.data.length);
+ }
+
+ state->params_total = parms->in.params.length;
+ state->data_total = parms->in.data.length;
+
+ SCVAL(req->out.vwv, 0, parms->in.max_setup);
+ SSVAL(req->out.vwv, 1, 0); /* reserved */
+ SIVAL(req->out.vwv, 3, parms->in.params.length);
+ SIVAL(req->out.vwv, 7, parms->in.data.length);
+ SIVAL(req->out.vwv, 11, parms->in.max_param);
+ SIVAL(req->out.vwv, 15, parms->in.max_data);
+ SIVAL(req->out.vwv, 19, params_chunk.length);
+ SIVAL(req->out.vwv, 23, params_ofs);
+ SIVAL(req->out.vwv, 27, data_chunk.length);
+ SIVAL(req->out.vwv, 31, data_ofs);
+ SCVAL(req->out.vwv, 35, parms->in.setup_count);
+ SSVAL(req->out.vwv, 36, parms->in.function);
+ memcpy(req->out.vwv + VWV(19), parms->in.setup,
+ sizeof(uint16_t) * parms->in.setup_count);
+
+ smbcli_req_append_blob(req, &params_chunk);
+ smbcli_req_append_blob(req, &data_chunk);
+
+ /* add the helper which will check that all multi-part replies are
+ in before an async client callack will be issued */
+ req->recv_helper.fn = smb_raw_nttrans_recv_helper;
+ req->recv_helper.private_data = state;
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+static enum smbcli_request_state smb_raw_nttrans_ship_next(struct smbcli_request *req,
+ struct smb_raw_nttrans_recv_state *state)
+{
+ struct smbcli_request *req2;
+ size_t space_left;
+ DATA_BLOB params_chunk;
+ uint32_t ofs;
+ uint32_t params_ofs = 0;
+ uint32_t params_disp = 0;
+ DATA_BLOB data_chunk;
+ uint32_t data_ofs = 0;
+ uint32_t data_disp = 0;
+
+ req2 = smbcli_request_setup(req->tree, SMBnttranss, 18, 0);
+ if (!req2) {
+ goto nomem;
+ }
+ req2->mid = req->mid;
+ SSVAL(req2->out.hdr, HDR_MID, req2->mid);
+
+ ofs = PTR_DIFF(req2->out.data,req2->out.hdr);
+
+ /* see how much bytes of the params block we can ship in the first request */
+ space_left = raw_trans_space_left(req2);
+
+ params_disp = state->io.in.params.length - state->params_left;
+ params_chunk.length = MIN(state->params_left, space_left);
+ params_chunk.data = state->io.in.params.data + params_disp;
+ params_ofs = ofs;
+
+ state->params_left -= params_chunk.length;
+
+ /* see how much bytes of the data block we can ship in the first request */
+ space_left -= params_chunk.length;
+
+#if TORTURE_TRANS_DATA
+ if (space_left > 1) {
+ space_left /= 2;
+ }
+#endif
+
+ data_disp = state->io.in.data.length - state->data_left;
+ data_chunk.length = MIN(state->data_left, space_left);
+ data_chunk.data = state->io.in.data.data + data_disp;
+ data_ofs = params_ofs+params_chunk.length;
+
+ state->data_left -= data_chunk.length;
+
+ SSVAL(req2->out.vwv,0, 0); /* reserved */
+ SCVAL(req2->out.vwv,2, 0); /* reserved */
+ SIVAL(req2->out.vwv,3, state->params_total);
+ SIVAL(req2->out.vwv,7, state->data_total);
+ SIVAL(req2->out.vwv,11, params_chunk.length);
+ SIVAL(req2->out.vwv,15, params_ofs);
+ SIVAL(req2->out.vwv,19, params_disp);
+ SIVAL(req2->out.vwv,23, data_chunk.length);
+ SIVAL(req2->out.vwv,27, data_ofs);
+ SIVAL(req2->out.vwv,31, data_disp);
+ SCVAL(req2->out.vwv,35, 0); /* reserved */
+
+ smbcli_req_append_blob(req2, &params_chunk);
+ smbcli_req_append_blob(req2, &data_chunk);
+
+ /*
+ * it's a one way request but we need
+ * the seq_num, so we destroy req2 by hand
+ */
+ if (!smbcli_request_send(req2)) {
+ goto failed;
+ }
+
+ req->seq_num = req2->seq_num;
+ smbcli_request_destroy(req2);
+
+ return SMBCLI_REQUEST_RECV;
+
+nomem:
+ req->status = NT_STATUS_NO_MEMORY;
+failed:
+ if (req2) {
+ req->status = smbcli_request_destroy(req2);
+ }
+ return SMBCLI_REQUEST_ERROR;
+}
+
+static enum smbcli_request_state smb_raw_nttrans_ship_rest(struct smbcli_request *req,
+ struct smb_raw_nttrans_recv_state *state)
+{
+ enum smbcli_request_state ret = SMBCLI_REQUEST_ERROR;
+
+ while (state->params_left > 0 || state->data_left > 0) {
+ ret = smb_raw_nttrans_ship_next(req, state);
+ if (ret != SMBCLI_REQUEST_RECV) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+/****************************************************************************
+ receive a SMB nttrans response allocating the necessary memory
+ ****************************************************************************/
+NTSTATUS smb_raw_nttrans(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_nttrans *parms)
+{
+ struct smbcli_request *req;
+
+ req = smb_raw_nttrans_send(tree, parms);
+ if (!req) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return smb_raw_nttrans_recv(req, mem_ctx, parms);
+}
diff --git a/source4/libcli/raw/request.h b/source4/libcli/raw/request.h
new file mode 100644
index 0000000000..2a572e58ee
--- /dev/null
+++ b/source4/libcli/raw/request.h
@@ -0,0 +1,78 @@
+#ifndef _REQUEST_H
+#define _REQUEST_H
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James Myers 2003 <myersjj@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 "libcli/raw/signing.h"
+
+#define BUFINFO_FLAG_UNICODE 0x0001
+#define BUFINFO_FLAG_SMB2 0x0002
+
+/*
+ buffer limit structure used by both SMB and SMB2
+ */
+struct request_bufinfo {
+ TALLOC_CTX *mem_ctx;
+ uint32_t flags;
+ const uint8_t *align_base;
+ const uint8_t *data;
+ size_t data_size;
+};
+
+/*
+ Shared state structure between client and server, representing the basic packet.
+*/
+
+struct smb_request_buffer {
+ /* the raw SMB buffer, including the 4 byte length header */
+ uint8_t *buffer;
+
+ /* the size of the raw buffer, including 4 byte header */
+ size_t size;
+
+ /* how much has been allocated - on reply the buffer is over-allocated to
+ prevent too many realloc() calls
+ */
+ size_t allocated;
+
+ /* the start of the SMB header - this is always buffer+4 */
+ uint8_t *hdr;
+
+ /* the command words and command word count. vwv points
+ into the raw buffer */
+ uint8_t *vwv;
+ uint_t wct;
+
+ /* the data buffer and size. data points into the raw buffer */
+ uint8_t *data;
+ size_t data_size;
+
+ /* ptr is used as a moving pointer into the data area
+ * of the packet. The reason its here and not a local
+ * variable in each function is that when a realloc of
+ * a send packet is done we need to move this
+ * pointer */
+ uint8_t *ptr;
+
+ /* this is used to range check and align strings and buffers */
+ struct request_bufinfo bufinfo;
+};
+
+#endif
diff --git a/source4/libcli/raw/signing.h b/source4/libcli/raw/signing.h
new file mode 100644
index 0000000000..56e977ed7c
--- /dev/null
+++ b/source4/libcli/raw/signing.h
@@ -0,0 +1,43 @@
+#ifndef _SIGNING_H
+#define _SIGNING_H
+/*
+ Unix SMB/CIFS implementation.
+ SMB Signing
+
+ Andrew Bartlett <abartlet@samba.org> 2003-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/>.
+*/
+
+enum smb_signing_engine_state {
+ SMB_SIGNING_ENGINE_OFF,
+ SMB_SIGNING_ENGINE_BSRSPYL,
+ SMB_SIGNING_ENGINE_ON
+};
+
+enum smb_signing_state {
+ SMB_SIGNING_OFF, SMB_SIGNING_SUPPORTED,
+ SMB_SIGNING_REQUIRED, SMB_SIGNING_AUTO};
+
+struct smb_signing_context {
+ enum smb_signing_engine_state signing_state;
+ DATA_BLOB mac_key;
+ uint32_t next_seq_num;
+ bool allow_smb_signing;
+ bool doing_signing;
+ bool mandatory_signing;
+ bool seen_valid; /* Have I ever seen a validly signed packet? */
+};
+
+#endif
diff --git a/source4/libcli/raw/smb.h b/source4/libcli/raw/smb.h
new file mode 100644
index 0000000000..d4091acf48
--- /dev/null
+++ b/source4/libcli/raw/smb.h
@@ -0,0 +1,622 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup, plus a whole lot more.
+
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) John H Terpstra 1996-2002
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1998-2000
+ Copyright (C) Simo Sorce 2001-2002
+ Copyright (C) Martin Pool 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 _SMB_H
+#define _SMB_H
+
+/* deny modes */
+#define DENY_DOS 0
+#define DENY_ALL 1
+#define DENY_WRITE 2
+#define DENY_READ 3
+#define DENY_NONE 4
+#define DENY_FCB 7
+
+/* open modes */
+#define DOS_OPEN_RDONLY 0
+#define DOS_OPEN_WRONLY 1
+#define DOS_OPEN_RDWR 2
+#define DOS_OPEN_FCB 0xF
+
+
+/**********************************/
+/* SMBopen field definitions */
+#define OPEN_FLAGS_DENY_MASK 0x70
+#define OPEN_FLAGS_DENY_DOS 0x00
+#define OPEN_FLAGS_DENY_ALL 0x10
+#define OPEN_FLAGS_DENY_WRITE 0x20
+#define OPEN_FLAGS_DENY_READ 0x30
+#define OPEN_FLAGS_DENY_NONE 0x40
+
+#define OPEN_FLAGS_MODE_MASK 0x0F
+#define OPEN_FLAGS_OPEN_READ 0
+#define OPEN_FLAGS_OPEN_WRITE 1
+#define OPEN_FLAGS_OPEN_RDWR 2
+#define OPEN_FLAGS_FCB 0xFF
+
+
+/**********************************/
+/* SMBopenX field definitions */
+
+/* OpenX Flags field. */
+#define OPENX_FLAGS_ADDITIONAL_INFO 0x01
+#define OPENX_FLAGS_REQUEST_OPLOCK 0x02
+#define OPENX_FLAGS_REQUEST_BATCH_OPLOCK 0x04
+#define OPENX_FLAGS_EA_LEN 0x08
+#define OPENX_FLAGS_EXTENDED_RETURN 0x10
+
+/* desired access (open_mode), split info 4 4-bit nibbles */
+#define OPENX_MODE_ACCESS_MASK 0x000F
+#define OPENX_MODE_ACCESS_READ 0x0000
+#define OPENX_MODE_ACCESS_WRITE 0x0001
+#define OPENX_MODE_ACCESS_RDWR 0x0002
+#define OPENX_MODE_ACCESS_EXEC 0x0003
+#define OPENX_MODE_ACCESS_FCB 0x000F
+
+#define OPENX_MODE_DENY_SHIFT 4
+#define OPENX_MODE_DENY_MASK (0xF << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_DOS (DENY_DOS << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_ALL (DENY_ALL << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_WRITE (DENY_WRITE << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_READ (DENY_READ << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_NONE (DENY_NONE << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_FCB (0xF << OPENX_MODE_DENY_SHIFT)
+
+#define OPENX_MODE_LOCALITY_MASK 0x0F00 /* what does this do? */
+
+#define OPENX_MODE_NO_CACHE 0x1000
+#define OPENX_MODE_WRITE_THRU 0x4000
+
+/* open function values */
+#define OPENX_OPEN_FUNC_MASK 0x3
+#define OPENX_OPEN_FUNC_FAIL 0x0
+#define OPENX_OPEN_FUNC_OPEN 0x1
+#define OPENX_OPEN_FUNC_TRUNC 0x2
+
+/* The above can be OR'ed with... */
+#define OPENX_OPEN_FUNC_CREATE 0x10
+
+/* openx action in reply */
+#define OPENX_ACTION_EXISTED 1
+#define OPENX_ACTION_CREATED 2
+#define OPENX_ACTION_TRUNCATED 3
+
+
+/**********************************/
+/* SMBntcreateX field definitions */
+
+/* ntcreatex flags field. */
+#define NTCREATEX_FLAGS_REQUEST_OPLOCK 0x02
+#define NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK 0x04
+#define NTCREATEX_FLAGS_OPEN_DIRECTORY 0x08 /* TODO: opens parent? we need
+ a test suite for this */
+#define NTCREATEX_FLAGS_EXTENDED 0x10
+
+/* the ntcreatex access_mask field
+ this is split into 4 pieces
+ AAAABBBBCCCCCCCCDDDDDDDDDDDDDDDD
+ A -> GENERIC_RIGHT_*
+ B -> SEC_RIGHT_*
+ C -> STD_RIGHT_*
+ D -> SA_RIGHT_*
+
+ which set of SA_RIGHT_* bits is applicable depends on the type
+ of object.
+*/
+
+
+
+/* ntcreatex share_access field */
+#define NTCREATEX_SHARE_ACCESS_NONE 0
+#define NTCREATEX_SHARE_ACCESS_READ 1
+#define NTCREATEX_SHARE_ACCESS_WRITE 2
+#define NTCREATEX_SHARE_ACCESS_DELETE 4
+#define NTCREATEX_SHARE_ACCESS_MASK 7
+
+/* ntcreatex open_disposition field */
+#define NTCREATEX_DISP_SUPERSEDE 0 /* supersede existing file (if it exists) */
+#define NTCREATEX_DISP_OPEN 1 /* if file exists open it, else fail */
+#define NTCREATEX_DISP_CREATE 2 /* if file exists fail, else create it */
+#define NTCREATEX_DISP_OPEN_IF 3 /* if file exists open it, else create it */
+#define NTCREATEX_DISP_OVERWRITE 4 /* if exists overwrite, else fail */
+#define NTCREATEX_DISP_OVERWRITE_IF 5 /* if exists overwrite, else create */
+
+/* ntcreatex create_options field */
+#define NTCREATEX_OPTIONS_DIRECTORY 0x0001
+#define NTCREATEX_OPTIONS_WRITE_THROUGH 0x0002
+#define NTCREATEX_OPTIONS_SEQUENTIAL_ONLY 0x0004
+#define NTCREATEX_OPTIONS_NO_INTERMEDIATE_BUFFERING 0x0008
+#define NTCREATEX_OPTIONS_SYNC_ALERT 0x0010
+#define NTCREATEX_OPTIONS_ASYNC_ALERT 0x0020
+#define NTCREATEX_OPTIONS_NON_DIRECTORY_FILE 0x0040
+#define NTCREATEX_OPTIONS_TREE_CONNECTION 0x0080
+#define NTCREATEX_OPTIONS_COMPLETE_IF_OPLOCKED 0x0100
+#define NTCREATEX_OPTIONS_NO_EA_KNOWLEDGE 0x0200
+#define NTCREATEX_OPTIONS_OPEN_FOR_RECOVERY 0x0400
+#define NTCREATEX_OPTIONS_RANDOM_ACCESS 0x0800
+#define NTCREATEX_OPTIONS_DELETE_ON_CLOSE 0x1000
+#define NTCREATEX_OPTIONS_OPEN_BY_FILE_ID 0x2000
+#define NTCREATEX_OPTIONS_BACKUP_INTENT 0x4000
+#define NTCREATEX_OPTIONS_NO_COMPRESSION 0x8000
+/* Must be ignored by the server, per MS-SMB 2.2.8 */
+#define NTCREATEX_OPTIONS_OPFILTER 0x00100000
+#define NTCREATEX_OPTIONS_REPARSE_POINT 0x00200000
+/* Don't pull this file off tape in a HSM system */
+#define NTCREATEX_OPTIONS_NO_RECALL 0x00400000
+/* Must be ignored by the server, per MS-SMB 2.2.8 */
+#define NTCREATEX_OPTIONS_FREE_SPACE_QUERY 0x00800000
+
+#define NTCREATEX_OPTIONS_MUST_IGNORE_MASK (NTCREATEX_OPTIONS_TREE_CONNECTION | \
+ NTCREATEX_OPTIONS_OPEN_FOR_RECOVERY | \
+ NTCREATEX_OPTIONS_FREE_SPACE_QUERY | \
+ 0x000F0000)
+
+#define NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK (NTCREATEX_OPTIONS_OPEN_BY_FILE_ID)
+
+#define NTCREATEX_OPTIONS_INVALID_PARAM_MASK (NTCREATEX_OPTIONS_OPFILTER | \
+ NTCREATEX_OPTIONS_SYNC_ALERT | \
+ NTCREATEX_OPTIONS_ASYNC_ALERT | \
+ NTCREATEX_OPTIONS_OPFILTER | \
+ 0xFF000000)
+
+/*
+ * We reuse some ignored flags for private use.
+ * This values have different meaning for some ntvfs backends.
+ *
+ * TODO: use values that are ignore for sure...
+ */
+#define NTCREATEX_OPTIONS_PRIVATE_DENY_DOS 0x00010000
+#define NTCREATEX_OPTIONS_PRIVATE_DENY_FCB 0x00020000
+#define NTCREATEX_OPTIONS_PRIVATE_MASK (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | \
+ NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)
+
+/* ntcreatex impersonation field */
+#define NTCREATEX_IMPERSONATION_ANONYMOUS 0
+#define NTCREATEX_IMPERSONATION_IDENTIFICATION 1
+#define NTCREATEX_IMPERSONATION_IMPERSONATION 2
+#define NTCREATEX_IMPERSONATION_DELEGATION 3
+
+/* ntcreatex security flags bit field */
+#define NTCREATEX_SECURITY_DYNAMIC 1
+#define NTCREATEX_SECURITY_ALL 2
+
+/* ntcreatex create_action in reply */
+#define NTCREATEX_ACTION_EXISTED 1
+#define NTCREATEX_ACTION_CREATED 2
+#define NTCREATEX_ACTION_TRUNCATED 3
+/* the value 5 can also be returned when you try to create a directory with
+ incorrect parameters - what does it mean? maybe created temporary file? */
+#define NTCREATEX_ACTION_UNKNOWN 5
+
+#define SMB_MAGIC 0x424D53FF /* 0xFF 'S' 'M' 'B' */
+
+/* the basic packet size, assuming no words or bytes. Does not include the NBT header */
+#define MIN_SMB_SIZE 35
+
+/* when using NBT encapsulation every packet has a 4 byte header */
+#define NBT_HDR_SIZE 4
+
+/* offsets into message header for common items - NOTE: These have
+ changed from being offsets from the base of the NBT packet to the base of the SMB packet.
+ this has reduced all these values by 4
+*/
+#define HDR_COM 4
+#define HDR_RCLS 5
+#define HDR_REH 6
+#define HDR_ERR 7
+#define HDR_FLG 9
+#define HDR_FLG2 10
+#define HDR_PIDHIGH 12
+#define HDR_SS_FIELD 14
+#define HDR_TID 24
+#define HDR_PID 26
+#define HDR_UID 28
+#define HDR_MID 30
+#define HDR_WCT 32
+#define HDR_VWV 33
+
+
+/* types of buffers in core SMB protocol */
+#define SMB_DATA_BLOCK 0x1
+#define SMB_ASCII4 0x4
+
+
+/* flag defines. CIFS spec 3.1.1 */
+#define FLAG_SUPPORT_LOCKREAD 0x01
+#define FLAG_CLIENT_BUF_AVAIL 0x02
+#define FLAG_RESERVED 0x04
+#define FLAG_CASELESS_PATHNAMES 0x08
+#define FLAG_CANONICAL_PATHNAMES 0x10
+#define FLAG_REQUEST_OPLOCK 0x20
+#define FLAG_REQUEST_BATCH_OPLOCK 0x40
+#define FLAG_REPLY 0x80
+
+/* the complete */
+#define SMBmkdir 0x00 /* create directory */
+#define SMBrmdir 0x01 /* delete directory */
+#define SMBopen 0x02 /* open file */
+#define SMBcreate 0x03 /* create file */
+#define SMBclose 0x04 /* close file */
+#define SMBflush 0x05 /* flush file */
+#define SMBunlink 0x06 /* delete file */
+#define SMBmv 0x07 /* rename file */
+#define SMBgetatr 0x08 /* get file attributes */
+#define SMBsetatr 0x09 /* set file attributes */
+#define SMBread 0x0A /* read from file */
+#define SMBwrite 0x0B /* write to file */
+#define SMBlock 0x0C /* lock byte range */
+#define SMBunlock 0x0D /* unlock byte range */
+#define SMBctemp 0x0E /* create temporary file */
+#define SMBmknew 0x0F /* make new file */
+#define SMBchkpth 0x10 /* check directory path */
+#define SMBexit 0x11 /* process exit */
+#define SMBlseek 0x12 /* seek */
+#define SMBtcon 0x70 /* tree connect */
+#define SMBtconX 0x75 /* tree connect and X*/
+#define SMBtdis 0x71 /* tree disconnect */
+#define SMBnegprot 0x72 /* negotiate protocol */
+#define SMBdskattr 0x80 /* get disk attributes */
+#define SMBsearch 0x81 /* search directory */
+#define SMBsplopen 0xC0 /* open print spool file */
+#define SMBsplwr 0xC1 /* write to print spool file */
+#define SMBsplclose 0xC2 /* close print spool file */
+#define SMBsplretq 0xC3 /* return print queue */
+#define SMBsends 0xD0 /* send single block message */
+#define SMBsendb 0xD1 /* send broadcast message */
+#define SMBfwdname 0xD2 /* forward user name */
+#define SMBcancelf 0xD3 /* cancel forward */
+#define SMBgetmac 0xD4 /* get machine name */
+#define SMBsendstrt 0xD5 /* send start of multi-block message */
+#define SMBsendend 0xD6 /* send end of multi-block message */
+#define SMBsendtxt 0xD7 /* send text of multi-block message */
+
+/* Core+ protocol */
+#define SMBlockread 0x13 /* Lock a range and read */
+#define SMBwriteunlock 0x14 /* write then range then unlock it */
+#define SMBreadbraw 0x1a /* read a block of data with no smb header */
+#define SMBwritebraw 0x1d /* write a block of data with no smb header */
+#define SMBwritec 0x20 /* secondary write request */
+#define SMBwriteclose 0x2c /* write a file then close it */
+
+/* dos extended protocol */
+#define SMBreadBraw 0x1A /* read block raw */
+#define SMBreadBmpx 0x1B /* read block multiplexed */
+#define SMBreadBs 0x1C /* read block (secondary response) */
+#define SMBwriteBraw 0x1D /* write block raw */
+#define SMBwriteBmpx 0x1E /* write block multiplexed */
+#define SMBwriteBs 0x1F /* write block (secondary request) */
+#define SMBwriteC 0x20 /* write complete response */
+#define SMBsetattrE 0x22 /* set file attributes expanded */
+#define SMBgetattrE 0x23 /* get file attributes expanded */
+#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */
+#define SMBtrans 0x25 /* transaction - name, bytes in/out */
+#define SMBtranss 0x26 /* transaction (secondary request/response) */
+#define SMBioctl 0x27 /* IOCTL */
+#define SMBioctls 0x28 /* IOCTL (secondary request/response) */
+#define SMBcopy 0x29 /* copy */
+#define SMBmove 0x2A /* move */
+#define SMBecho 0x2B /* echo */
+#define SMBopenX 0x2D /* open and X */
+#define SMBreadX 0x2E /* read and X */
+#define SMBwriteX 0x2F /* write and X */
+#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */
+#define SMBffirst 0x82 /* find first */
+#define SMBfunique 0x83 /* find unique */
+#define SMBfclose 0x84 /* find close */
+#define SMBkeepalive 0x85 /* keepalive */
+#define SMBinvalid 0xFE /* invalid command */
+
+/* Extended 2.0 protocol */
+#define SMBtrans2 0x32 /* TRANS2 protocol set */
+#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */
+#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */
+#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */
+#define SMBulogoffX 0x74 /* user logoff */
+
+/* NT SMB extensions. */
+#define SMBnttrans 0xA0 /* NT transact */
+#define SMBnttranss 0xA1 /* NT transact secondary */
+#define SMBntcreateX 0xA2 /* NT create and X */
+#define SMBntcancel 0xA4 /* NT cancel */
+#define SMBntrename 0xA5 /* NT rename */
+
+/* used to indicate end of chain */
+#define SMB_CHAIN_NONE 0xFF
+
+/* These are the trans subcommands */
+#define TRANSACT_SETNAMEDPIPEHANDLESTATE 0x01
+#define TRANSACT_DCERPCCMD 0x26
+#define TRANSACT_WAITNAMEDPIPEHANDLESTATE 0x53
+
+/* These are the NT transact sub commands. */
+#define NT_TRANSACT_CREATE 1
+#define NT_TRANSACT_IOCTL 2
+#define NT_TRANSACT_SET_SECURITY_DESC 3
+#define NT_TRANSACT_NOTIFY_CHANGE 4
+#define NT_TRANSACT_RENAME 5
+#define NT_TRANSACT_QUERY_SECURITY_DESC 6
+
+/* this is used on a TConX. I'm not sure the name is very helpful though */
+#define SMB_SUPPORT_SEARCH_BITS 0x0001
+#define SMB_SHARE_IN_DFS 0x0002
+
+/* Named pipe write mode flags. Used in writeX calls. */
+#define PIPE_RAW_MODE 0x4
+#define PIPE_START_MESSAGE 0x8
+
+/* the desired access to use when opening a pipe */
+#define DESIRED_ACCESS_PIPE 0x2019f
+
+
+/* Mapping of generic access rights for files to specific rights. */
+#define FILE_GENERIC_ALL (STANDARD_RIGHTS_REQUIRED_ACCESS| NT_ACCESS_SYNCHRONIZE_ACCESS|FILE_ALL_ACCESS)
+
+#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ_ACCESS|FILE_READ_DATA|FILE_READ_ATTRIBUTES|\
+ FILE_READ_EA|NT_ACCESS_SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE_ACCESS|FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|\
+ FILE_WRITE_EA|FILE_APPEND_DATA|NT_ACCESS_SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE_ACCESS|FILE_READ_ATTRIBUTES|\
+ FILE_EXECUTE|NT_ACCESS_SYNCHRONIZE_ACCESS)
+
+
+/* FileAttributes (search attributes) field */
+#define FILE_ATTRIBUTE_READONLY 0x0001
+#define FILE_ATTRIBUTE_HIDDEN 0x0002
+#define FILE_ATTRIBUTE_SYSTEM 0x0004
+#define FILE_ATTRIBUTE_VOLUME 0x0008
+#define FILE_ATTRIBUTE_DIRECTORY 0x0010
+#define FILE_ATTRIBUTE_ARCHIVE 0x0020
+#define FILE_ATTRIBUTE_DEVICE 0x0040
+#define FILE_ATTRIBUTE_NORMAL 0x0080
+#define FILE_ATTRIBUTE_TEMPORARY 0x0100
+#define FILE_ATTRIBUTE_SPARSE 0x0200
+#define FILE_ATTRIBUTE_REPARSE_POINT 0x0400
+#define FILE_ATTRIBUTE_COMPRESSED 0x0800
+#define FILE_ATTRIBUTE_OFFLINE 0x1000
+#define FILE_ATTRIBUTE_NONINDEXED 0x2000
+#define FILE_ATTRIBUTE_ENCRYPTED 0x4000
+#define FILE_ATTRIBUTE_ALL_MASK 0x7FFF
+
+/* Flags - combined with attributes. */
+#define FILE_FLAG_WRITE_THROUGH 0x80000000L
+#define FILE_FLAG_NO_BUFFERING 0x20000000L
+#define FILE_FLAG_RANDOM_ACCESS 0x10000000L
+#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000L
+#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000L
+#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000L /* only if backup/restore privilege? */
+#define FILE_FLAG_POSIX_SEMANTICS 0x01000000L
+
+/* Responses when opening a file. */
+#define FILE_WAS_SUPERSEDED 0
+#define FILE_WAS_OPENED 1
+#define FILE_WAS_CREATED 2
+#define FILE_WAS_OVERWRITTEN 3
+
+/* File type flags */
+#define FILE_TYPE_DISK 0
+#define FILE_TYPE_BYTE_MODE_PIPE 1
+#define FILE_TYPE_MESSAGE_MODE_PIPE 2
+#define FILE_TYPE_PRINTER 3
+#define FILE_TYPE_COMM_DEVICE 4
+#define FILE_TYPE_UNKNOWN 0xFFFF
+
+/* Flag for NT transact rename call. */
+#define RENAME_REPLACE_IF_EXISTS 1
+
+/* flags for SMBntrename call */
+#define RENAME_FLAG_MOVE_CLUSTER_INFORMATION 0x102 /* ???? */
+#define RENAME_FLAG_HARD_LINK 0x103
+#define RENAME_FLAG_RENAME 0x104
+#define RENAME_FLAG_COPY 0x105
+
+/* Filesystem Attributes. */
+#define FILE_CASE_SENSITIVE_SEARCH 0x01
+#define FILE_CASE_PRESERVED_NAMES 0x02
+#define FILE_UNICODE_ON_DISK 0x04
+/* According to cifs9f, this is 4, not 8 */
+/* Acconding to testing, this actually sets the security attribute! */
+#define FILE_PERSISTENT_ACLS 0x08
+/* These entries added from cifs9f --tsb */
+#define FILE_FILE_COMPRESSION 0x10
+#define FILE_VOLUME_QUOTAS 0x20
+/* I think this is wrong. JRA #define FILE_DEVICE_IS_MOUNTED 0x20 */
+#define FILE_VOLUME_SPARSE_FILE 0x40
+#define FILE_VOLUME_IS_COMPRESSED 0x8000
+
+/* ChangeNotify flags. */
+#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001
+#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002
+#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004
+#define FILE_NOTIFY_CHANGE_SIZE 0x00000008
+#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010
+#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020
+#define FILE_NOTIFY_CHANGE_CREATION 0x00000040
+#define FILE_NOTIFY_CHANGE_EA 0x00000080
+#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100
+#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200
+#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400
+#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800
+
+#define FILE_NOTIFY_CHANGE_NAME \
+ (FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME)
+
+/* change notify action results */
+#define NOTIFY_ACTION_ADDED 1
+#define NOTIFY_ACTION_REMOVED 2
+#define NOTIFY_ACTION_MODIFIED 3
+#define NOTIFY_ACTION_OLD_NAME 4
+#define NOTIFY_ACTION_NEW_NAME 5
+#define NOTIFY_ACTION_ADDED_STREAM 6
+#define NOTIFY_ACTION_REMOVED_STREAM 7
+#define NOTIFY_ACTION_MODIFIED_STREAM 8
+
+/* seek modes for smb_seek */
+#define SEEK_MODE_START 0
+#define SEEK_MODE_CURRENT 1
+#define SEEK_MODE_END 2
+
+/* where to find the base of the SMB packet proper */
+/* REWRITE TODO: smb_base needs to be removed */
+#define smb_base(buf) (((char *)(buf))+4)
+
+/* we don't allow server strings to be longer than 48 characters as
+ otherwise NT will not honour the announce packets */
+#define MAX_SERVER_STRING_LENGTH 48
+
+/* This was set by JHT in liaison with Jeremy Allison early 1997
+ * History:
+ * Version 4.0 - never made public
+ * Version 4.10 - New to 1.9.16p2, lost in space 1.9.16p3 to 1.9.16p9
+ * - Reappeared in 1.9.16p11 with fixed smbd services
+ * Version 4.20 - To indicate that nmbd and browsing now works better
+ * Version 4.50 - Set at release of samba-2.2.0 by JHT
+ *
+ * Note: In the presence of NT4.X do not set above 4.9
+ * Setting this above 4.9 can have undesired side-effects.
+ * This may change again in Samba-3.0 after further testing. JHT
+ */
+
+#define DEFAULT_MAJOR_VERSION 0x04
+#define DEFAULT_MINOR_VERSION 0x09
+
+/* Browser Election Values */
+#define BROWSER_ELECTION_VERSION 0x010f
+#define BROWSER_CONSTANT 0xaa55
+
+/* Sercurity mode bits. */
+#define NEGOTIATE_SECURITY_USER_LEVEL 0x01
+#define NEGOTIATE_SECURITY_CHALLENGE_RESPONSE 0x02
+#define NEGOTIATE_SECURITY_SIGNATURES_ENABLED 0x04
+#define NEGOTIATE_SECURITY_SIGNATURES_REQUIRED 0x08
+
+/* NT Flags2 bits - cifs6.txt section 3.1.2 */
+#define FLAGS2_LONG_PATH_COMPONENTS 0x0001
+#define FLAGS2_EXTENDED_ATTRIBUTES 0x0002
+#define FLAGS2_SMB_SECURITY_SIGNATURES 0x0004
+#define FLAGS2_IS_LONG_NAME 0x0040
+#define FLAGS2_EXTENDED_SECURITY 0x0800
+#define FLAGS2_DFS_PATHNAMES 0x1000
+#define FLAGS2_READ_PERMIT_EXECUTE 0x2000
+#define FLAGS2_32_BIT_ERROR_CODES 0x4000
+#define FLAGS2_UNICODE_STRINGS 0x8000
+
+
+/* CIFS protocol capabilities */
+#define CAP_RAW_MODE 0x00000001
+#define CAP_MPX_MODE 0x00000002
+#define CAP_UNICODE 0x00000004
+#define CAP_LARGE_FILES 0x00000008
+#define CAP_NT_SMBS 0x00000010
+#define CAP_RPC_REMOTE_APIS 0x00000020
+#define CAP_STATUS32 0x00000040
+#define CAP_LEVEL_II_OPLOCKS 0x00000080
+#define CAP_LOCK_AND_READ 0x00000100
+#define CAP_NT_FIND 0x00000200
+#define CAP_DFS 0x00001000
+#define CAP_W2K_SMBS 0x00002000
+#define CAP_LARGE_READX 0x00004000
+#define CAP_LARGE_WRITEX 0x00008000
+#define CAP_UNIX 0x00800000 /* Capabilities for UNIX extensions. Created by HP. */
+#define CAP_EXTENDED_SECURITY 0x80000000
+
+/*
+ * Global value meaning that the smb_uid field should be
+ * ingored (in share level security and protocol level == CORE)
+ */
+
+#define UID_FIELD_INVALID 0
+
+/* Lock types. */
+#define LOCKING_ANDX_SHARED_LOCK 0x01
+#define LOCKING_ANDX_OPLOCK_RELEASE 0x02
+#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04
+#define LOCKING_ANDX_CANCEL_LOCK 0x08
+#define LOCKING_ANDX_LARGE_FILES 0x10
+
+/*
+ * Bits we test with.
+ */
+
+#define OPLOCK_NONE 0
+#define OPLOCK_EXCLUSIVE 1
+#define OPLOCK_BATCH 2
+#define OPLOCK_LEVEL_II 4
+
+#define CORE_OPLOCK_GRANTED (1<<5)
+#define EXTENDED_OPLOCK_GRANTED (1<<15)
+
+/*
+ * Return values for oplock types.
+ */
+
+#define NO_OPLOCK_RETURN 0
+#define EXCLUSIVE_OPLOCK_RETURN 1
+#define BATCH_OPLOCK_RETURN 2
+#define LEVEL_II_OPLOCK_RETURN 3
+
+/* oplock levels sent in oplock break */
+#define OPLOCK_BREAK_TO_NONE 0
+#define OPLOCK_BREAK_TO_LEVEL_II 1
+
+
+#define CMD_REPLY 0x8000
+
+/* The maximum length of a trust account password.
+ Used when we randomly create it, 15 char passwords
+ exceed NT4's max password length */
+
+#define DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH 14
+
+
+/*
+ filesystem attribute bits
+*/
+#define FS_ATTR_CASE_SENSITIVE_SEARCH 0x00000001
+#define FS_ATTR_CASE_PRESERVED_NAMES 0x00000002
+#define FS_ATTR_UNICODE_ON_DISK 0x00000004
+#define FS_ATTR_PERSISTANT_ACLS 0x00000008
+#define FS_ATTR_COMPRESSION 0x00000010
+#define FS_ATTR_QUOTAS 0x00000020
+#define FS_ATTR_SPARSE_FILES 0x00000040
+#define FS_ATTR_REPARSE_POINTS 0x00000080
+#define FS_ATTR_REMOTE_STORAGE 0x00000100
+#define FS_ATTR_LFN_SUPPORT 0x00004000
+#define FS_ATTR_IS_COMPRESSED 0x00008000
+#define FS_ATTR_OBJECT_IDS 0x00010000
+#define FS_ATTR_ENCRYPTION 0x00020000
+#define FS_ATTR_NAMED_STREAMS 0x00040000
+
+#define smb_len(buf) (PVAL(buf,3)|(PVAL(buf,2)<<8)|(PVAL(buf,1)<<16))
+#define _smb_setlen(buf,len) do {(buf)[0] = 0; (buf)[1] = ((len)&0x10000)>>16; \
+ (buf)[2] = ((len)&0xFF00)>>8; (buf)[3] = (len)&0xFF;} while (0)
+#define _smb2_setlen(buf,len) do {(buf)[0] = 0; (buf)[1] = ((len)&0xFF0000)>>16; \
+ (buf)[2] = ((len)&0xFF00)>>8; (buf)[3] = (len)&0xFF;} while (0)
+
+#include "libcli/raw/trans2.h"
+#include "libcli/raw/interfaces.h"
+
+#endif /* _SMB_H */
diff --git a/source4/libcli/raw/smb_signing.c b/source4/libcli/raw/smb_signing.c
new file mode 100644
index 0000000000..9f94039078
--- /dev/null
+++ b/source4/libcli/raw/smb_signing.c
@@ -0,0 +1,407 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB Signing Code
+ Copyright (C) Jeremy Allison 2002.
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+ Copyright (C) James J Myers <myersjj@samba.org> 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 "smb.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "../lib/crypto/crypto.h"
+
+/***********************************************************
+ SMB signing - Common code before we set a new signing implementation
+************************************************************/
+bool set_smb_signing_common(struct smb_signing_context *sign_info)
+{
+ if (sign_info->doing_signing) {
+ DEBUG(5, ("SMB Signing already in progress, so we don't start it again\n"));
+ return false;
+ }
+
+ if (!sign_info->allow_smb_signing) {
+ DEBUG(5, ("SMB Signing has been locally disabled\n"));
+ return false;
+ }
+
+ return true;
+}
+
+/***********************************************************
+ SMB signing - Common code before we set a new signing implementation
+************************************************************/
+static bool smbcli_set_smb_signing_common(struct smbcli_transport *transport)
+{
+ if (!set_smb_signing_common(&transport->negotiate.sign_info)) {
+ return false;
+ }
+
+ if (!(transport->negotiate.sec_mode &
+ (NEGOTIATE_SECURITY_SIGNATURES_REQUIRED|NEGOTIATE_SECURITY_SIGNATURES_ENABLED))) {
+ DEBUG(5, ("SMB Signing is not negotiated by the peer\n"));
+ return false;
+ }
+
+ /* These calls are INCOMPATIBLE with SMB signing */
+ transport->negotiate.readbraw_supported = false;
+ transport->negotiate.writebraw_supported = false;
+
+ return true;
+}
+
+void mark_packet_signed(struct smb_request_buffer *out)
+{
+ uint16_t flags2;
+ flags2 = SVAL(out->hdr, HDR_FLG2);
+ flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+ SSVAL(out->hdr, HDR_FLG2, flags2);
+}
+
+bool signing_good(struct smb_signing_context *sign_info,
+ unsigned int seq, bool good)
+{
+ if (good) {
+ if (!sign_info->doing_signing) {
+ DEBUG(5, ("Seen valid packet, so turning signing on\n"));
+ sign_info->doing_signing = true;
+ }
+ if (!sign_info->seen_valid) {
+ DEBUG(5, ("Seen valid packet, so marking signing as 'seen valid'\n"));
+ sign_info->seen_valid = true;
+ }
+ } else {
+ if (!sign_info->seen_valid) {
+ /* If we have never seen a good packet, just turn it off */
+ DEBUG(5, ("signing_good: signing negotiated but not required and peer\n"
+ "isn't sending correct signatures. Turning off.\n"));
+ smbcli_set_signing_off(sign_info);
+ return true;
+ } else {
+ /* bad packet after signing started - fail and disconnect. */
+ DEBUG(0, ("signing_good: BAD SIG: seq %u\n", seq));
+ return false;
+ }
+ }
+ return true;
+}
+
+void sign_outgoing_message(struct smb_request_buffer *out, DATA_BLOB *mac_key, unsigned int seq_num)
+{
+ uint8_t calc_md5_mac[16];
+ struct MD5Context md5_ctx;
+
+ /*
+ * Firstly put the sequence number into the first 4 bytes.
+ * and zero out the next 4 bytes.
+ */
+ SIVAL(out->hdr, HDR_SS_FIELD, seq_num);
+ SIVAL(out->hdr, HDR_SS_FIELD + 4, 0);
+
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(out);
+
+ /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, mac_key->data, mac_key->length);
+ MD5Update(&md5_ctx,
+ out->buffer + NBT_HDR_SIZE,
+ out->size - NBT_HDR_SIZE);
+ MD5Final(calc_md5_mac, &md5_ctx);
+
+ memcpy(&out->hdr[HDR_SS_FIELD], calc_md5_mac, 8);
+
+ DEBUG(5, ("sign_outgoing_message: SENT SIG (seq: %d): sent SMB signature of\n",
+ seq_num));
+ dump_data(5, calc_md5_mac, 8);
+/* req->out.hdr[HDR_SS_FIELD+2]=0;
+ Uncomment this to test if the remote server actually verifies signitures...*/
+}
+
+bool check_signed_incoming_message(struct smb_request_buffer *in, DATA_BLOB *mac_key, uint_t seq_num)
+{
+ bool good;
+ uint8_t calc_md5_mac[16];
+ uint8_t *server_sent_mac;
+ uint8_t sequence_buf[8];
+ struct MD5Context md5_ctx;
+ const size_t offset_end_of_sig = (HDR_SS_FIELD + 8);
+ int i;
+ const int sign_range = 0;
+
+ /* room enough for the signature? */
+ if (in->size < NBT_HDR_SIZE + HDR_SS_FIELD + 8) {
+ return false;
+ }
+
+ if (!mac_key->length) {
+ /* NO key yet */
+ return false;
+ }
+
+ /* its quite bogus to be guessing sequence numbers, but very useful
+ when debugging signing implementations */
+ for (i = 0-sign_range; i <= 0+sign_range; i++) {
+ /*
+ * Firstly put the sequence number into the first 4 bytes.
+ * and zero out the next 4 bytes.
+ */
+ SIVAL(sequence_buf, 0, seq_num + i);
+ SIVAL(sequence_buf, 4, 0);
+
+ /* get a copy of the server-sent mac */
+ server_sent_mac = &in->hdr[HDR_SS_FIELD];
+
+ /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, mac_key->data,
+ mac_key->length);
+ MD5Update(&md5_ctx, in->hdr, HDR_SS_FIELD);
+ MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
+
+ MD5Update(&md5_ctx, in->hdr + offset_end_of_sig,
+ in->size - NBT_HDR_SIZE - (offset_end_of_sig));
+ MD5Final(calc_md5_mac, &md5_ctx);
+
+ good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
+
+ if (i == 0) {
+ if (!good) {
+ DEBUG(5, ("check_signed_incoming_message: BAD SIG (seq: %d): wanted SMB signature of\n", seq_num + i));
+ dump_data(5, calc_md5_mac, 8);
+
+ DEBUG(5, ("check_signed_incoming_message: BAD SIG (seq: %d): got SMB signature of\n", seq_num + i));
+ dump_data(5, server_sent_mac, 8);
+ } else {
+ DEBUG(15, ("check_signed_incoming_message: GOOD SIG (seq: %d): got SMB signature of\n", seq_num + i));
+ dump_data(5, server_sent_mac, 8);
+ }
+ }
+
+ if (good) break;
+ }
+
+ if (good && i != 0) {
+ DEBUG(0,("SIGNING OFFSET %d (should be %d)\n", i, seq_num));
+ }
+
+ return good;
+}
+
+static void smbcli_req_allocate_seq_num(struct smbcli_request *req)
+{
+ req->seq_num = req->transport->negotiate.sign_info.next_seq_num;
+
+ /* some requests (eg. NTcancel) are one way, and the sequence number
+ should be increased by 1 not 2 */
+ if (req->sign_single_increment) {
+ req->transport->negotiate.sign_info.next_seq_num += 1;
+ } else {
+ req->transport->negotiate.sign_info.next_seq_num += 2;
+ }
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - calculate a MAC to send.
+************************************************************/
+void smbcli_request_calculate_sign_mac(struct smbcli_request *req)
+{
+#if 0
+ /* enable this when packet signing is preventing you working out why valgrind
+ says that data is uninitialised */
+ file_save("pkt.dat", req->out.buffer, req->out.size);
+#endif
+
+ switch (req->transport->negotiate.sign_info.signing_state) {
+ case SMB_SIGNING_ENGINE_OFF:
+ break;
+
+ case SMB_SIGNING_ENGINE_BSRSPYL:
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(&req->out);
+
+ /* I wonder what BSRSPYL stands for - but this is what MS
+ actually sends! */
+ memcpy((req->out.hdr + HDR_SS_FIELD), "BSRSPYL ", 8);
+ break;
+
+ case SMB_SIGNING_ENGINE_ON:
+
+ smbcli_req_allocate_seq_num(req);
+ sign_outgoing_message(&req->out,
+ &req->transport->negotiate.sign_info.mac_key,
+ req->seq_num);
+ break;
+ }
+ return;
+}
+
+
+/**
+ SMB signing - NULL implementation
+
+ @note Used as an initialisation only - it will not correctly
+ shut down a real signing mechanism
+*/
+bool smbcli_set_signing_off(struct smb_signing_context *sign_info)
+{
+ DEBUG(5, ("Shutdown SMB signing\n"));
+ sign_info->doing_signing = false;
+ data_blob_free(&sign_info->mac_key);
+ sign_info->signing_state = SMB_SIGNING_ENGINE_OFF;
+ return true;
+}
+
+/**
+ SMB signing - TEMP implementation - setup the MAC key.
+
+*/
+bool smbcli_temp_set_signing(struct smbcli_transport *transport)
+{
+ if (!smbcli_set_smb_signing_common(transport)) {
+ return false;
+ }
+ DEBUG(5, ("BSRSPYL SMB signing enabled\n"));
+ smbcli_set_signing_off(&transport->negotiate.sign_info);
+
+ transport->negotiate.sign_info.mac_key = data_blob(NULL, 0);
+ transport->negotiate.sign_info.signing_state = SMB_SIGNING_ENGINE_BSRSPYL;
+
+ return true;
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - check a MAC sent by server.
+************************************************************/
+/**
+ * Check a packet supplied by the server.
+ * @return false if we had an established signing connection
+ * which had a back checksum, true otherwise
+ */
+bool smbcli_request_check_sign_mac(struct smbcli_request *req)
+{
+ bool good;
+
+ if (!req->transport->negotiate.sign_info.doing_signing &&
+ req->sign_caller_checks) {
+ return true;
+ }
+
+ req->sign_caller_checks = false;
+
+ switch (req->transport->negotiate.sign_info.signing_state)
+ {
+ case SMB_SIGNING_ENGINE_OFF:
+ return true;
+ case SMB_SIGNING_ENGINE_BSRSPYL:
+ return true;
+
+ case SMB_SIGNING_ENGINE_ON:
+ {
+ if (req->in.size < (HDR_SS_FIELD + 8)) {
+ return false;
+ } else {
+ good = check_signed_incoming_message(&req->in,
+ &req->transport->negotiate.sign_info.mac_key,
+ req->seq_num+1);
+
+ return signing_good(&req->transport->negotiate.sign_info,
+ req->seq_num+1, good);
+ }
+ }
+ }
+ return false;
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - setup the MAC key.
+************************************************************/
+bool smbcli_simple_set_signing(TALLOC_CTX *mem_ctx,
+ struct smb_signing_context *sign_info,
+ const DATA_BLOB *user_session_key,
+ const DATA_BLOB *response)
+{
+ if (sign_info->mandatory_signing) {
+ DEBUG(5, ("Mandatory SMB signing enabled!\n"));
+ }
+
+ DEBUG(5, ("SMB signing enabled!\n"));
+
+ if (response && response->length) {
+ sign_info->mac_key = data_blob_talloc(mem_ctx, NULL, response->length + user_session_key->length);
+ } else {
+ sign_info->mac_key = data_blob_talloc(mem_ctx, NULL, user_session_key->length);
+ }
+
+ memcpy(&sign_info->mac_key.data[0], user_session_key->data, user_session_key->length);
+
+ if (response && response->length) {
+ memcpy(&sign_info->mac_key.data[user_session_key->length],response->data, response->length);
+ }
+
+ dump_data_pw("Started Signing with key:\n", sign_info->mac_key.data, sign_info->mac_key.length);
+
+ sign_info->signing_state = SMB_SIGNING_ENGINE_ON;
+ sign_info->next_seq_num = 2;
+
+ return true;
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - setup the MAC key.
+************************************************************/
+bool smbcli_transport_simple_set_signing(struct smbcli_transport *transport,
+ const DATA_BLOB user_session_key,
+ const DATA_BLOB response)
+{
+ if (!smbcli_set_smb_signing_common(transport)) {
+ return false;
+ }
+
+ return smbcli_simple_set_signing(transport,
+ &transport->negotiate.sign_info,
+ &user_session_key,
+ &response);
+}
+
+
+bool smbcli_init_signing(struct smbcli_transport *transport)
+{
+ transport->negotiate.sign_info.next_seq_num = 0;
+ transport->negotiate.sign_info.mac_key = data_blob(NULL, 0);
+ if (!smbcli_set_signing_off(&transport->negotiate.sign_info)) {
+ return false;
+ }
+
+ switch (transport->options.signing) {
+ case SMB_SIGNING_OFF:
+ transport->negotiate.sign_info.allow_smb_signing = false;
+ break;
+ case SMB_SIGNING_SUPPORTED:
+ case SMB_SIGNING_AUTO:
+ transport->negotiate.sign_info.allow_smb_signing = true;
+ break;
+ case SMB_SIGNING_REQUIRED:
+ transport->negotiate.sign_info.allow_smb_signing = true;
+ transport->negotiate.sign_info.mandatory_signing = true;
+ break;
+ }
+ return true;
+}
diff --git a/source4/libcli/raw/trans2.h b/source4/libcli/raw/trans2.h
new file mode 100644
index 0000000000..63632eb5ed
--- /dev/null
+++ b/source4/libcli/raw/trans2.h
@@ -0,0 +1,476 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB transaction2 handling
+ Copyright (C) Jeremy Allison 1994-2002.
+ Copyright (C) Andrew Tridgell 1995-2003.
+ Copyright (C) James Peach 2007
+
+ 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 _TRANS2_H_
+#define _TRANS2_H_
+
+/* These are the TRANS2 sub commands */
+#define TRANSACT2_OPEN 0
+#define TRANSACT2_FINDFIRST 1
+#define TRANSACT2_FINDNEXT 2
+#define TRANSACT2_QFSINFO 3
+#define TRANSACT2_SETFSINFO 4
+#define TRANSACT2_QPATHINFO 5
+#define TRANSACT2_SETPATHINFO 6
+#define TRANSACT2_QFILEINFO 7
+#define TRANSACT2_SETFILEINFO 8
+#define TRANSACT2_FSCTL 9
+#define TRANSACT2_IOCTL 0xA
+#define TRANSACT2_FINDNOTIFYFIRST 0xB
+#define TRANSACT2_FINDNOTIFYNEXT 0xC
+#define TRANSACT2_MKDIR 0xD
+#define TRANSACT2_SESSION_SETUP 0xE
+#define TRANSACT2_GET_DFS_REFERRAL 0x10
+#define TRANSACT2_REPORT_DFS_INCONSISTANCY 0x11
+
+
+/* trans2 Query FS info levels */
+/*
+w2k3 TRANS2ALIASES:
+Checking for QFSINFO aliases
+ Found level 1 (0x001) of size 18 (0x12)
+ Found level 2 (0x002) of size 12 (0x0c)
+ Found level 258 (0x102) of size 26 (0x1a)
+ Found level 259 (0x103) of size 24 (0x18)
+ Found level 260 (0x104) of size 8 (0x08)
+ Found level 261 (0x105) of size 20 (0x14)
+ Found level 1001 (0x3e9) of size 26 (0x1a)
+ Found level 1003 (0x3eb) of size 24 (0x18)
+ Found level 1004 (0x3ec) of size 8 (0x08)
+ Found level 1005 (0x3ed) of size 20 (0x14)
+ Found level 1006 (0x3ee) of size 48 (0x30)
+ Found level 1007 (0x3ef) of size 32 (0x20)
+ Found level 1008 (0x3f0) of size 64 (0x40)
+Found 13 levels with success status
+ Level 261 (0x105) and level 1005 (0x3ed) are possible aliases
+ Level 260 (0x104) and level 1004 (0x3ec) are possible aliases
+ Level 259 (0x103) and level 1003 (0x3eb) are possible aliases
+ Level 258 (0x102) and level 1001 (0x3e9) are possible aliases
+Found 4 aliased levels
+*/
+#define SMB_QFS_ALLOCATION 1
+#define SMB_QFS_VOLUME 2
+#define SMB_QFS_VOLUME_INFO 0x102
+#define SMB_QFS_SIZE_INFO 0x103
+#define SMB_QFS_DEVICE_INFO 0x104
+#define SMB_QFS_ATTRIBUTE_INFO 0x105
+#define SMB_QFS_UNIX_INFO 0x200
+#define SMB_QFS_POSIX_INFO 0x201
+#define SMB_QFS_POSIX_WHOAMI 0x202
+#define SMB_QFS_VOLUME_INFORMATION 1001
+#define SMB_QFS_SIZE_INFORMATION 1003
+#define SMB_QFS_DEVICE_INFORMATION 1004
+#define SMB_QFS_ATTRIBUTE_INFORMATION 1005
+#define SMB_QFS_QUOTA_INFORMATION 1006
+#define SMB_QFS_FULL_SIZE_INFORMATION 1007
+#define SMB_QFS_OBJECTID_INFORMATION 1008
+
+
+/* trans2 qfileinfo/qpathinfo */
+/* w2k3 TRANS2ALIASES:
+Checking for QPATHINFO aliases
+setting up complex file \qpathinfo_aliases.txt
+ Found level 1 (0x001) of size 22 (0x16)
+ Found level 2 (0x002) of size 26 (0x1a)
+ Found level 4 (0x004) of size 41 (0x29)
+ Found level 6 (0x006) of size 0 (0x00)
+ Found level 257 (0x101) of size 40 (0x28)
+ Found level 258 (0x102) of size 24 (0x18)
+ Found level 259 (0x103) of size 4 (0x04)
+ Found level 260 (0x104) of size 48 (0x30)
+ Found level 263 (0x107) of size 126 (0x7e)
+ Found level 264 (0x108) of size 28 (0x1c)
+ Found level 265 (0x109) of size 38 (0x26)
+ Found level 267 (0x10b) of size 16 (0x10)
+ Found level 1004 (0x3ec) of size 40 (0x28)
+ Found level 1005 (0x3ed) of size 24 (0x18)
+ Found level 1006 (0x3ee) of size 8 (0x08)
+ Found level 1007 (0x3ef) of size 4 (0x04)
+ Found level 1008 (0x3f0) of size 4 (0x04)
+ Found level 1009 (0x3f1) of size 48 (0x30)
+ Found level 1014 (0x3f6) of size 8 (0x08)
+ Found level 1016 (0x3f8) of size 4 (0x04)
+ Found level 1017 (0x3f9) of size 4 (0x04)
+ Found level 1018 (0x3fa) of size 126 (0x7e)
+ Found level 1021 (0x3fd) of size 28 (0x1c)
+ Found level 1022 (0x3fe) of size 38 (0x26)
+ Found level 1028 (0x404) of size 16 (0x10)
+ Found level 1034 (0x40a) of size 56 (0x38)
+ Found level 1035 (0x40b) of size 8 (0x08)
+Found 27 levels with success status
+ Level 267 (0x10b) and level 1028 (0x404) are possible aliases
+ Level 265 (0x109) and level 1022 (0x3fe) are possible aliases
+ Level 264 (0x108) and level 1021 (0x3fd) are possible aliases
+ Level 263 (0x107) and level 1018 (0x3fa) are possible aliases
+ Level 260 (0x104) and level 1009 (0x3f1) are possible aliases
+ Level 259 (0x103) and level 1007 (0x3ef) are possible aliases
+ Level 258 (0x102) and level 1005 (0x3ed) are possible aliases
+ Level 257 (0x101) and level 1004 (0x3ec) are possible aliases
+Found 8 aliased levels
+*/
+#define SMB_QFILEINFO_STANDARD 1
+#define SMB_QFILEINFO_EA_SIZE 2
+#define SMB_QFILEINFO_EA_LIST 3
+#define SMB_QFILEINFO_ALL_EAS 4
+#define SMB_QFILEINFO_IS_NAME_VALID 6 /* only for QPATHINFO */
+#define SMB_QFILEINFO_BASIC_INFO 0x101
+#define SMB_QFILEINFO_STANDARD_INFO 0x102
+#define SMB_QFILEINFO_EA_INFO 0x103
+#define SMB_QFILEINFO_NAME_INFO 0x104
+#define SMB_QFILEINFO_ALL_INFO 0x107
+#define SMB_QFILEINFO_ALT_NAME_INFO 0x108
+#define SMB_QFILEINFO_STREAM_INFO 0x109
+#define SMB_QFILEINFO_COMPRESSION_INFO 0x10b
+#define SMB_QFILEINFO_UNIX_BASIC 0x200
+#define SMB_QFILEINFO_UNIX_LINK 0x201
+#define SMB_QFILEINFO_UNIX_INFO2 0x20b
+#define SMB_QFILEINFO_BASIC_INFORMATION 1004
+#define SMB_QFILEINFO_STANDARD_INFORMATION 1005
+#define SMB_QFILEINFO_INTERNAL_INFORMATION 1006
+#define SMB_QFILEINFO_EA_INFORMATION 1007
+#define SMB_QFILEINFO_ACCESS_INFORMATION 1008
+#define SMB_QFILEINFO_NAME_INFORMATION 1009
+#define SMB_QFILEINFO_POSITION_INFORMATION 1014
+#define SMB_QFILEINFO_MODE_INFORMATION 1016
+#define SMB_QFILEINFO_ALIGNMENT_INFORMATION 1017
+#define SMB_QFILEINFO_ALL_INFORMATION 1018
+#define SMB_QFILEINFO_ALT_NAME_INFORMATION 1021
+#define SMB_QFILEINFO_STREAM_INFORMATION 1022
+#define SMB_QFILEINFO_COMPRESSION_INFORMATION 1028
+#define SMB_QFILEINFO_NETWORK_OPEN_INFORMATION 1034
+#define SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION 1035
+
+
+
+/* trans2 setfileinfo/setpathinfo levels */
+/*
+w2k3 TRANS2ALIASES
+Checking for SETFILEINFO aliases
+setting up complex file \setfileinfo_aliases.txt
+ Found level 1 (0x001) of size 2 (0x02)
+ Found level 2 (0x002) of size 2 (0x02)
+ Found level 257 (0x101) of size 40 (0x28)
+ Found level 258 (0x102) of size 2 (0x02)
+ Found level 259 (0x103) of size 8 (0x08)
+ Found level 260 (0x104) of size 8 (0x08)
+ Found level 1004 (0x3ec) of size 40 (0x28)
+ Found level 1010 (0x3f2) of size 2 (0x02)
+ Found level 1013 (0x3f5) of size 2 (0x02)
+ Found level 1014 (0x3f6) of size 8 (0x08)
+ Found level 1016 (0x3f8) of size 4 (0x04)
+ Found level 1019 (0x3fb) of size 8 (0x08)
+ Found level 1020 (0x3fc) of size 8 (0x08)
+ Found level 1023 (0x3ff) of size 8 (0x08)
+ Found level 1025 (0x401) of size 16 (0x10)
+ Found level 1029 (0x405) of size 72 (0x48)
+ Found level 1032 (0x408) of size 56 (0x38)
+ Found level 1039 (0x40f) of size 8 (0x08)
+ Found level 1040 (0x410) of size 8 (0x08)
+Found 19 valid levels
+
+Checking for SETPATHINFO aliases
+ Found level 1004 (0x3ec) of size 40 (0x28)
+ Found level 1010 (0x3f2) of size 2 (0x02)
+ Found level 1013 (0x3f5) of size 2 (0x02)
+ Found level 1014 (0x3f6) of size 8 (0x08)
+ Found level 1016 (0x3f8) of size 4 (0x04)
+ Found level 1019 (0x3fb) of size 8 (0x08)
+ Found level 1020 (0x3fc) of size 8 (0x08)
+ Found level 1023 (0x3ff) of size 8 (0x08)
+ Found level 1025 (0x401) of size 16 (0x10)
+ Found level 1029 (0x405) of size 72 (0x48)
+ Found level 1032 (0x408) of size 56 (0x38)
+ Found level 1039 (0x40f) of size 8 (0x08)
+ Found level 1040 (0x410) of size 8 (0x08)
+Found 13 valid levels
+*/
+#define SMB_SFILEINFO_STANDARD 1
+#define SMB_SFILEINFO_EA_SET 2
+#define SMB_SFILEINFO_BASIC_INFO 0x101
+#define SMB_SFILEINFO_DISPOSITION_INFO 0x102
+#define SMB_SFILEINFO_ALLOCATION_INFO 0x103
+#define SMB_SFILEINFO_END_OF_FILE_INFO 0x104
+#define SMB_SFILEINFO_UNIX_BASIC 0x200
+#define SMB_SFILEINFO_UNIX_LINK 0x201
+#define SMB_SPATHINFO_UNIX_HLINK 0x203
+#define SMB_SPATHINFO_POSIX_ACL 0x204
+#define SMB_SPATHINFO_XATTR 0x205
+#define SMB_SFILEINFO_ATTR_FLAGS 0x206
+#define SMB_SFILEINFO_UNIX_INFO2 0x20b
+#define SMB_SFILEINFO_BASIC_INFORMATION 1004
+#define SMB_SFILEINFO_RENAME_INFORMATION 1010
+#define SMB_SFILEINFO_LINK_INFORMATION 1011
+#define SMB_SFILEINFO_DISPOSITION_INFORMATION 1013
+#define SMB_SFILEINFO_POSITION_INFORMATION 1014
+#define SMB_SFILEINFO_FULL_EA_INFORMATION 1015
+#define SMB_SFILEINFO_MODE_INFORMATION 1016
+#define SMB_SFILEINFO_ALLOCATION_INFORMATION 1019
+#define SMB_SFILEINFO_END_OF_FILE_INFORMATION 1020
+#define SMB_SFILEINFO_PIPE_INFORMATION 1023
+#define SMB_SFILEINFO_VALID_DATA_INFORMATION 1039
+#define SMB_SFILEINFO_SHORT_NAME_INFORMATION 1040
+
+/* filemon shows FilePipeRemoteInformation */
+#define SMB_SFILEINFO_1025 1025
+
+/* vista scan responds */
+#define SMB_SFILEINFO_1027 1027
+
+/* filemon shows CopyOnWriteInformation */
+#define SMB_SFILEINFO_1029 1029
+
+/* filemon shows OleClassIdInformation */
+#define SMB_SFILEINFO_1032 1032
+
+/* vista scan responds to these */
+#define SMB_SFILEINFO_1030 1030
+#define SMB_SFILEINFO_1031 1031
+#define SMB_SFILEINFO_1036 1036
+#define SMB_SFILEINFO_1041 1041
+#define SMB_SFILEINFO_1042 1042
+#define SMB_SFILEINFO_1043 1043
+#define SMB_SFILEINFO_1044 1044
+
+/* trans2 findfirst levels */
+/*
+w2k3 TRANS2ALIASES:
+Checking for FINDFIRST aliases
+ Found level 1 (0x001) of size 68 (0x44)
+ Found level 2 (0x002) of size 70 (0x46)
+ Found level 257 (0x101) of size 108 (0x6c)
+ Found level 258 (0x102) of size 116 (0x74)
+ Found level 259 (0x103) of size 60 (0x3c)
+ Found level 260 (0x104) of size 140 (0x8c)
+ Found level 261 (0x105) of size 124 (0x7c)
+ Found level 262 (0x106) of size 148 (0x94)
+Found 8 levels with success status
+Found 0 aliased levels
+*/
+#define SMB_FIND_STANDARD 1
+#define SMB_FIND_EA_SIZE 2
+#define SMB_FIND_EA_LIST 3
+#define SMB_FIND_DIRECTORY_INFO 0x101
+#define SMB_FIND_FULL_DIRECTORY_INFO 0x102
+#define SMB_FIND_NAME_INFO 0x103
+#define SMB_FIND_BOTH_DIRECTORY_INFO 0x104
+#define SMB_FIND_ID_FULL_DIRECTORY_INFO 0x105
+#define SMB_FIND_ID_BOTH_DIRECTORY_INFO 0x106
+#define SMB_FIND_UNIX_INFO 0x202
+#define SMB_FIND_UNIX_INFO2 0x20b
+
+/* flags on trans2 findfirst/findnext that control search */
+#define FLAG_TRANS2_FIND_CLOSE 0x1
+#define FLAG_TRANS2_FIND_CLOSE_IF_END 0x2
+#define FLAG_TRANS2_FIND_REQUIRE_RESUME 0x4
+#define FLAG_TRANS2_FIND_CONTINUE 0x8
+#define FLAG_TRANS2_FIND_BACKUP_INTENT 0x10
+
+/*
+ * DeviceType and Characteristics returned in a
+ * SMB_QFS_DEVICE_INFO call.
+ */
+#define QFS_DEVICETYPE_CD_ROM 0x2
+#define QFS_DEVICETYPE_CD_ROM_FILE_SYSTEM 0x3
+#define QFS_DEVICETYPE_DISK 0x7
+#define QFS_DEVICETYPE_DISK_FILE_SYSTEM 0x8
+#define QFS_DEVICETYPE_FILE_SYSTEM 0x9
+
+/* Characteristics. */
+#define QFS_TYPE_REMOVABLE_MEDIA 0x1
+#define QFS_TYPE_READ_ONLY_DEVICE 0x2
+#define QFS_TYPE_FLOPPY 0x4
+#define QFS_TYPE_WORM 0x8
+#define QFS_TYPE_REMOTE 0x10
+#define QFS_TYPE_MOUNTED 0x20
+#define QFS_TYPE_VIRTUAL 0x40
+
+
+/*
+ * Thursby MAC extensions....
+ */
+
+/*
+ * MAC CIFS Extensions have the range 0x300 - 0x2FF reserved.
+ * Supposedly Microsoft have agreed to this.
+ */
+
+#define MIN_MAC_INFO_LEVEL 0x300
+#define MAX_MAC_INFO_LEVEL 0x3FF
+#define SMB_QFS_MAC_FS_INFO 0x301
+
+
+
+/* UNIX CIFS Extensions - created by HP */
+/*
+ * UNIX CIFS Extensions have the range 0x200 - 0x2FF reserved.
+ * Supposedly Microsoft have agreed to this.
+ */
+
+#define MIN_UNIX_INFO_LEVEL 0x200
+#define MAX_UNIX_INFO_LEVEL 0x2FF
+
+#define INFO_LEVEL_IS_UNIX(level) (((level) >= MIN_UNIX_INFO_LEVEL) && ((level) <= MAX_UNIX_INFO_LEVEL))
+
+#define SMB_MODE_NO_CHANGE 0xFFFFFFFF /* file mode value which */
+ /* means "don't change it" */
+#define SMB_UID_NO_CHANGE 0xFFFFFFFF
+#define SMB_GID_NO_CHANGE 0xFFFFFFFF
+
+#define SMB_SIZE_NO_CHANGE_LO 0xFFFFFFFF
+#define SMB_SIZE_NO_CHANGE_HI 0xFFFFFFFF
+
+#define SMB_TIME_NO_CHANGE_LO 0xFFFFFFFF
+#define SMB_TIME_NO_CHANGE_HI 0xFFFFFFFF
+
+/*
+UNIX_BASIC info level:
+
+Offset Size Name
+0 LARGE_INTEGER EndOfFile File size
+8 LARGE_INTEGER Blocks Number of bytes used on disk (st_blocks).
+16 LARGE_INTEGER CreationTime Creation time
+24 LARGE_INTEGER LastAccessTime Last access time
+32 LARGE_INTEGER LastModificationTime Last modification time
+40 LARGE_INTEGER Uid Numeric user id for the owner
+48 LARGE_INTEGER Gid Numeric group id of owner
+56 ULONG Type Enumeration specifying the pathname type:
+ 0 -- File
+ 1 -- Directory
+ 2 -- Symbolic link
+ 3 -- Character device
+ 4 -- Block device
+ 5 -- FIFO (named pipe)
+ 6 -- Unix domain socket
+
+60 LARGE_INTEGER devmajor Major device number if type is device
+68 LARGE_INTEGER devminor Minor device number if type is device
+76 LARGE_INTEGER uniqueid This is a server-assigned unique id for the file. The client
+ will typically map this onto an inode number. The scope of
+ uniqueness is the share.
+84 LARGE_INTEGER permissions Standard UNIX file permissions - see below.
+92 LARGE_INTEGER nlinks The number of directory entries that map to this entry
+ (number of hard links)
+
+100 - end.
+*/
+
+/*
+SMB_QUERY_FILE_UNIX_INFO2 is SMB_QUERY_FILE_UNIX_BASIC with create
+time and file flags appended. The corresponding info level for
+findfirst/findnext is SMB_FIND_FILE_UNIX_UNIX2.
+
+Size Offset Value
+---------------------
+0 LARGE_INTEGER EndOfFile File size
+8 LARGE_INTEGER Blocks Number of blocks used on disk
+16 LARGE_INTEGER ChangeTime Attribute change time
+24 LARGE_INTEGER LastAccessTime Last access time
+32 LARGE_INTEGER LastModificationTime Last modification time
+40 LARGE_INTEGER Uid Numeric user id for the owner
+48 LARGE_INTEGER Gid Numeric group id of owner
+56 ULONG Type Enumeration specifying the file type
+60 LARGE_INTEGER devmajor Major device number if type is device
+68 LARGE_INTEGER devminor Minor device number if type is device
+76 LARGE_INTEGER uniqueid This is a server-assigned unique id
+84 LARGE_INTEGER permissions Standard UNIX permissions
+92 LARGE_INTEGER nlinks Number of hard link)
+100 LARGE_INTEGER CreationTime Create/birth time
+108 ULONG FileFlags File flags enumeration
+112 ULONG FileFlagsMask Mask of valid flags
+*/
+
+/* UNIX filetype mappings. */
+
+#define UNIX_TYPE_FILE 0
+#define UNIX_TYPE_DIR 1
+#define UNIX_TYPE_SYMLINK 2
+#define UNIX_TYPE_CHARDEV 3
+#define UNIX_TYPE_BLKDEV 4
+#define UNIX_TYPE_FIFO 5
+#define UNIX_TYPE_SOCKET 6
+#define UNIX_TYPE_UNKNOWN 0xFFFFFFFF
+
+/*
+ * Oh this is fun. "Standard UNIX permissions" has no
+ * meaning in POSIX. We need to define the mapping onto
+ * and off the wire as this was not done in the original HP
+ * spec. JRA.
+ */
+
+#define UNIX_X_OTH 0000001
+#define UNIX_W_OTH 0000002
+#define UNIX_R_OTH 0000004
+#define UNIX_X_GRP 0000010
+#define UNIX_W_GRP 0000020
+#define UNIX_R_GRP 0000040
+#define UNIX_X_USR 0000100
+#define UNIX_W_USR 0000200
+#define UNIX_R_USR 0000400
+#define UNIX_STICKY 0001000
+#define UNIX_SET_GID 0002000
+#define UNIX_SET_UID 0004000
+
+/* Masks for the above */
+#define UNIX_OTH_MASK 0000007
+#define UNIX_GRP_MASK 0000070
+#define UNIX_USR_MASK 0000700
+#define UNIX_PERM_MASK 0000777
+#define UNIX_EXTRA_MASK 0007000
+#define UNIX_ALL_MASK 0007777
+
+/* Flags for the file_flags field in UNIX_INFO2: */
+#define EXT_SECURE_DELETE 0x00000001
+#define EXT_ENABLE_UNDELETE 0x00000002
+#define EXT_SYNCHRONOUS 0x00000004
+#define EXT_IMMUTABLE 0x00000008
+#define EXT_OPEN_APPEND_ONLY 0x00000010
+#define EXT_DO_NOT_BACKUP 0x00000020
+#define EXT_NO_UPDATE_ATIME 0x00000040
+#define EXT_HIDDEN 0x00000080
+
+#define SMB_QFILEINFO_UNIX_LINK 0x201
+#define SMB_SFILEINFO_UNIX_LINK 0x201
+#define SMB_SFILEINFO_UNIX_HLINK 0x203
+
+/*
+ Info level for QVOLINFO - returns version of CIFS UNIX extensions, plus
+ 64-bits worth of capability fun :-).
+*/
+
+#define SMB_QUERY_CIFS_UNIX_INFO 0x200
+
+/* Returns the following.
+
+ UINT16 major version number
+ UINT16 minor version number
+ LARGE_INTEGER capability bitfield
+
+*/
+
+#define CIFS_UNIX_MAJOR_VERSION 1
+#define CIFS_UNIX_MINOR_VERSION 0
+
+#define CIFS_UNIX_FCNTL_LOCKS_CAP 0x1
+#define CIFS_UNIX_POSIX_ACLS_CAP 0x2
+
+/* ... more as we think of them :-). */
+
+#endif
diff --git a/source4/libcli/resolve/bcast.c b/source4/libcli/resolve/bcast.c
new file mode 100644
index 0000000000..da58eb8665
--- /dev/null
+++ b/source4/libcli/resolve/bcast.c
@@ -0,0 +1,106 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ broadcast name resolution module
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Jelmer Vernooij 2007
+
+ 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/resolve/resolve.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+
+struct resolve_bcast_data {
+ struct interface *ifaces;
+ uint16_t nbt_port;
+ int nbt_timeout;
+};
+
+/**
+ broadcast name resolution method - async send
+ */
+struct composite_context *resolve_name_bcast_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ void *userdata, uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name)
+{
+ int num_interfaces;
+ const char **address_list;
+ struct composite_context *c;
+ int i, count=0;
+ struct resolve_bcast_data *data = talloc_get_type(userdata, struct resolve_bcast_data);
+
+ num_interfaces = iface_count(data->ifaces);
+
+ address_list = talloc_array(mem_ctx, const char *, num_interfaces+1);
+ if (address_list == NULL) return NULL;
+
+ for (i=0;i<num_interfaces;i++) {
+ const char *bcast = iface_n_bcast(data->ifaces, i);
+ if (bcast == NULL) continue;
+ address_list[count] = talloc_strdup(address_list, bcast);
+ if (address_list[count] == NULL) {
+ talloc_free(address_list);
+ return NULL;
+ }
+ count++;
+ }
+ address_list[count] = NULL;
+
+ c = resolve_name_nbtlist_send(mem_ctx, event_ctx, flags, port, name,
+ address_list, data->ifaces, data->nbt_port,
+ data->nbt_timeout, true, false);
+ talloc_free(address_list);
+
+ return c;
+}
+
+/*
+ broadcast name resolution method - recv side
+ */
+NTSTATUS resolve_name_bcast_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names)
+{
+ NTSTATUS status = resolve_name_nbtlist_recv(c, mem_ctx, addrs, names);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ /* this makes much more sense for a bcast name resolution
+ timeout */
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ return status;
+}
+
+bool resolve_context_add_bcast_method(struct resolve_context *ctx, struct interface *ifaces, uint16_t nbt_port, int nbt_timeout)
+{
+ struct resolve_bcast_data *data = talloc(ctx, struct resolve_bcast_data);
+ data->ifaces = ifaces;
+ data->nbt_port = nbt_port;
+ data->nbt_timeout = nbt_timeout;
+ return resolve_context_add_method(ctx, resolve_name_bcast_send, resolve_name_bcast_recv, data);
+}
+
+bool resolve_context_add_bcast_method_lp(struct resolve_context *ctx, struct loadparm_context *lp_ctx)
+{
+ struct interface *ifaces;
+ load_interfaces(ctx, lp_interfaces(lp_ctx), &ifaces);
+ return resolve_context_add_bcast_method(ctx, ifaces, lp_nbt_port(lp_ctx), lp_parm_int(lp_ctx, NULL, "nbt", "timeout", 1));
+}
diff --git a/source4/libcli/resolve/dns_ex.c b/source4/libcli/resolve/dns_ex.c
new file mode 100644
index 0000000000..ce41d0cea8
--- /dev/null
+++ b/source4/libcli/resolve/dns_ex.c
@@ -0,0 +1,541 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ async getaddrinfo()/dns_lookup() name resolution module
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher 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/>.
+*/
+
+/*
+ this module uses a fork() per getaddrinfo() or dns_looup() call.
+ At first that might seem crazy, but it is actually very fast,
+ and solves many of the tricky problems of keeping a child
+ hanging around in a librar (like what happens when the parent forks).
+ We use a talloc destructor to ensure that the child is cleaned up
+ when we have finished with this name resolution.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "system/network.h"
+#include "system/filesys.h"
+#include "lib/socket/socket.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "libcli/resolve/resolve.h"
+
+#ifdef class
+#undef class
+#endif
+
+#include "heimdal/lib/roken/resolve.h"
+
+struct dns_ex_state {
+ bool do_fallback;
+ uint32_t flags;
+ uint16_t port;
+ struct nbt_name name;
+ struct socket_address **addrs;
+ char **names;
+ pid_t child;
+ int child_fd;
+ struct tevent_fd *fde;
+ struct tevent_context *event_ctx;
+};
+
+/*
+ kill off a wayward child if needed. This allows us to stop an async
+ name resolution without leaving a potentially blocking call running
+ in a child
+*/
+static int dns_ex_destructor(struct dns_ex_state *state)
+{
+ int status;
+
+ kill(state->child, SIGTERM);
+ close(state->child_fd);
+ if (waitpid(state->child, &status, WNOHANG) == 0) {
+ kill(state->child, SIGKILL);
+ waitpid(state->child, &status, 0);
+ }
+
+ return 0;
+}
+
+/*
+ the blocking child
+*/
+static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
+{
+ struct dns_reply *reply;
+ struct resource_record *rr;
+ uint32_t count = 0;
+ uint32_t srv_valid = 0;
+ struct resource_record **srv_rr;
+ uint32_t addrs_valid = 0;
+ struct resource_record **addrs_rr;
+ char *addrs;
+ bool first;
+ uint32_t i;
+ bool do_srv = (state->flags & RESOLVE_NAME_FLAG_DNS_SRV);
+
+ /* this is the blocking call we are going to lots of trouble
+ to avoid in the parent */
+ reply = dns_lookup(state->name.name, do_srv?"SRV":"A");
+ if (!reply) {
+ goto done;
+ }
+
+ if (do_srv) {
+ dns_srv_order(reply);
+ }
+
+ /* Loop over all returned records and pick the "srv" records */
+ for (rr=reply->head; rr; rr=rr->next) {
+ /* we are only interested in the IN class */
+ if (rr->class != C_IN) {
+ continue;
+ }
+
+ if (do_srv) {
+ /* we are only interested in SRV records */
+ if (rr->type != T_SRV) {
+ continue;
+ }
+
+ /* verify we actually have a SRV record here */
+ if (!rr->u.srv) {
+ continue;
+ }
+
+ /* Verify we got a port */
+ if (rr->u.srv->port == 0) {
+ continue;
+ }
+ } else {
+ /* we are only interested in A records */
+ /* TODO: add AAAA support */
+ if (rr->type != T_A) {
+ continue;
+ }
+
+ /* verify we actually have a A record here */
+ if (!rr->u.a) {
+ continue;
+ }
+ }
+ count++;
+ }
+
+ if (count == 0) {
+ goto done;
+ }
+
+ srv_rr = talloc_zero_array(state,
+ struct resource_record *,
+ count);
+ if (!srv_rr) {
+ goto done;
+ }
+
+ addrs_rr = talloc_zero_array(state,
+ struct resource_record *,
+ count);
+ if (!addrs_rr) {
+ goto done;
+ }
+
+ /* Loop over all returned records and pick the records */
+ for (rr=reply->head;rr;rr=rr->next) {
+ /* we are only interested in the IN class */
+ if (rr->class != C_IN) {
+ continue;
+ }
+
+ if (do_srv) {
+ /* we are only interested in SRV records */
+ if (rr->type != T_SRV) {
+ continue;
+ }
+
+ /* verify we actually have a srv record here */
+ if (!rr->u.srv) {
+ continue;
+ }
+
+ /* Verify we got a port */
+ if (rr->u.srv->port == 0) {
+ continue;
+ }
+
+ srv_rr[srv_valid] = rr;
+ srv_valid++;
+ } else {
+ /* we are only interested in A records */
+ /* TODO: add AAAA support */
+ if (rr->type != T_A) {
+ continue;
+ }
+
+ /* verify we actually have a A record here */
+ if (!rr->u.a) {
+ continue;
+ }
+
+ addrs_rr[addrs_valid] = rr;
+ addrs_valid++;
+ }
+ }
+
+ for (i=0; i < srv_valid; i++) {
+ for (rr=reply->head;rr;rr=rr->next) {
+
+ if (rr->class != C_IN) {
+ continue;
+ }
+
+ /* we are only interested in SRV records */
+ if (rr->type != T_A) {
+ continue;
+ }
+
+ /* verify we actually have a srv record here */
+ if (strcmp(&srv_rr[i]->u.srv->target[0], rr->domain) != 0) {
+ continue;
+ }
+
+ addrs_rr[i] = rr;
+ addrs_valid++;
+ break;
+ }
+ }
+
+ if (addrs_valid == 0) {
+ goto done;
+ }
+
+ addrs = talloc_strdup(state, "");
+ if (!addrs) {
+ goto done;
+ }
+ first = true;
+ for (i=0; i < count; i++) {
+ uint16_t port;
+ if (!addrs_rr[i]) {
+ continue;
+ }
+
+ if (srv_rr[i] &&
+ (state->flags & RESOLVE_NAME_FLAG_OVERWRITE_PORT)) {
+ port = srv_rr[i]->u.srv->port;
+ } else {
+ port = state->port;
+ }
+
+ addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
+ first?"":",",
+ inet_ntoa(*addrs_rr[i]->u.a),
+ port,
+ addrs_rr[i]->domain);
+ if (!addrs) {
+ goto done;
+ }
+ first = false;
+ }
+
+ if (addrs) {
+ write(fd, addrs, talloc_get_size(addrs));
+ }
+
+done:
+ close(fd);
+}
+
+/*
+ the blocking child
+*/
+static void run_child_getaddrinfo(struct dns_ex_state *state, int fd)
+{
+ int ret;
+ struct addrinfo hints;
+ struct addrinfo *res;
+ struct addrinfo *res_list = NULL;
+ char *addrs;
+ bool first;
+
+ ZERO_STRUCT(hints);
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_INET;/* TODO: add AF_INET6 support */
+ hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
+
+ ret = getaddrinfo(state->name.name, "0", &hints, &res_list);
+#ifdef EAI_NODATA
+ if (ret == EAI_NODATA && state->do_fallback) {
+#else
+ if (ret == EAI_NONAME && state->do_fallback) {
+#endif
+ /* getaddrinfo() doesn't handle CNAME records */
+ run_child_dns_lookup(state, fd);
+ return;
+ }
+ if (ret != 0) {
+ goto done;
+ }
+
+ addrs = talloc_strdup(state, "");
+ if (!addrs) {
+ goto done;
+ }
+ first = true;
+ for (res = res_list; res; res = res->ai_next) {
+ struct sockaddr_in *in;
+
+ if (res->ai_family != AF_INET) {
+ continue;
+ }
+ in = (struct sockaddr_in *)res->ai_addr;
+
+ addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
+ first?"":",",
+ inet_ntoa(in->sin_addr),
+ state->port,
+ state->name.name);
+ if (!addrs) {
+ goto done;
+ }
+ first = false;
+ }
+
+ if (addrs) {
+ write(fd, addrs, talloc_get_size(addrs));
+ }
+done:
+ if (res_list) {
+ freeaddrinfo(res_list);
+ }
+ close(fd);
+}
+
+/*
+ handle a read event on the pipe
+*/
+static void pipe_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct composite_context *c = talloc_get_type(private_data, struct composite_context);
+ struct dns_ex_state *state = talloc_get_type(c->private_data,
+ struct dns_ex_state);
+ char *address;
+ uint32_t num_addrs, i;
+ char **addrs;
+ int ret;
+ int status;
+ int value = 0;
+
+ /* if we get any event from the child then we know that we
+ won't need to kill it off */
+ talloc_set_destructor(state, NULL);
+
+ if (ioctl(state->child_fd, FIONREAD, &value) != 0) {
+ value = 8192;
+ }
+
+ address = talloc_array(state, char, value+1);
+ if (address) {
+ /* yes, we don't care about EAGAIN or other niceities
+ here. They just can't happen with this parent/child
+ relationship, and even if they did then giving an error is
+ the right thing to do */
+ ret = read(state->child_fd, address, value);
+ } else {
+ ret = -1;
+ }
+ close(state->child_fd);
+ if (waitpid(state->child, &status, WNOHANG) == 0) {
+ kill(state->child, SIGKILL);
+ waitpid(state->child, &status, 0);
+ }
+
+ if (ret <= 0) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ /* enusre the address looks good */
+ address[ret] = 0;
+
+ addrs = str_list_make(state, address, ",");
+ if (composite_nomem(addrs, c)) return;
+
+ num_addrs = str_list_length((const char * const *)addrs);
+
+ state->addrs = talloc_array(state, struct socket_address *,
+ num_addrs+1);
+ if (composite_nomem(state->addrs, c)) return;
+
+ state->names = talloc_array(state, char *, num_addrs+1);
+ if (composite_nomem(state->names, c)) return;
+
+ for (i=0; i < num_addrs; i++) {
+ uint32_t port = 0;
+ char *p = strrchr(addrs[i], ':');
+ char *n;
+
+ if (!p) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ *p = '\0';
+ p++;
+
+ n = strrchr(p, '/');
+ if (!n) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ *n = '\0';
+ n++;
+
+ if (strcmp(addrs[i], "0.0.0.0") == 0 ||
+ inet_addr(addrs[i]) == INADDR_NONE) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+ port = strtoul(p, NULL, 10);
+ if (port > UINT16_MAX) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+ state->addrs[i] = socket_address_from_strings(state->addrs,
+ "ipv4",
+ addrs[i],
+ port);
+ if (composite_nomem(state->addrs[i], c)) return;
+
+ state->names[i] = talloc_strdup(state->names, n);
+ if (composite_nomem(state->names[i], c)) return;
+ }
+ state->addrs[i] = NULL;
+ state->names[i] = NULL;
+
+ composite_done(c);
+}
+
+/*
+ getaddrinfo() or dns_lookup() name resolution method - async send
+ */
+struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ void *privdata,
+ uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name,
+ bool do_fallback)
+{
+ struct composite_context *c;
+ struct dns_ex_state *state;
+ int fd[2] = { -1, -1 };
+ int ret;
+
+ c = composite_create(mem_ctx, event_ctx);
+ if (c == NULL) return NULL;
+
+ if (flags & RESOLVE_NAME_FLAG_FORCE_NBT) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return c;
+ }
+
+ state = talloc_zero(c, struct dns_ex_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ c->status = nbt_name_dup(state, name, &state->name);
+ if (!composite_is_ok(c)) return c;
+
+ /* setup a pipe to chat to our child */
+ ret = pipe(fd);
+ if (ret == -1) {
+ composite_error(c, map_nt_error_from_unix(errno));
+ return c;
+ }
+
+ state->do_fallback = do_fallback;
+ state->flags = flags;
+ state->port = port;
+
+ state->child_fd = fd[0];
+ state->event_ctx = c->event_ctx;
+
+ /* we need to put the child in our event context so
+ we know when the dns_lookup() has finished */
+ state->fde = event_add_fd(c->event_ctx, c, state->child_fd, EVENT_FD_READ,
+ pipe_handler, c);
+ if (composite_nomem(state->fde, c)) {
+ close(fd[0]);
+ close(fd[1]);
+ return c;
+ }
+
+ state->child = fork();
+ if (state->child == (pid_t)-1) {
+ composite_error(c, map_nt_error_from_unix(errno));
+ return c;
+ }
+
+ if (state->child == 0) {
+ close(fd[0]);
+ if (state->flags & RESOLVE_NAME_FLAG_FORCE_DNS) {
+ run_child_dns_lookup(state, fd[1]);
+ } else {
+ run_child_getaddrinfo(state, fd[1]);
+ }
+ _exit(0);
+ }
+ close(fd[1]);
+
+ /* cleanup wayward children */
+ talloc_set_destructor(state, dns_ex_destructor);
+
+ return c;
+}
+
+/*
+ getaddrinfo() or dns_lookup() name resolution method - recv side
+*/
+NTSTATUS resolve_name_dns_ex_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct dns_ex_state *state = talloc_get_type(c->private_data,
+ struct dns_ex_state);
+ *addrs = talloc_steal(mem_ctx, state->addrs);
+ if (names) {
+ *names = talloc_steal(mem_ctx, state->names);
+ }
+ }
+
+ talloc_free(c);
+ return status;
+}
diff --git a/source4/libcli/resolve/host.c b/source4/libcli/resolve/host.c
new file mode 100644
index 0000000000..755a4e8304
--- /dev/null
+++ b/source4/libcli/resolve/host.c
@@ -0,0 +1,60 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ async "host" name resolution module
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher 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/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "system/network.h"
+#include "system/filesys.h"
+#include "lib/socket/socket.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "libcli/resolve/resolve.h"
+
+/*
+ getaddrinfo() (with fallback to dns_lookup()) name resolution method - async send
+ */
+struct composite_context *resolve_name_host_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ void *privdata, uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name)
+{
+ return resolve_name_dns_ex_send(mem_ctx, event_ctx, NULL, flags,
+ port, name, true);
+}
+
+/*
+ getaddrinfo() (with fallback to dns_lookup()) name resolution method - recv side
+*/
+NTSTATUS resolve_name_host_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names)
+{
+ return resolve_name_dns_ex_recv(c, mem_ctx, addrs, names);
+}
+
+bool resolve_context_add_host_method(struct resolve_context *ctx)
+{
+ return resolve_context_add_method(ctx, resolve_name_host_send, resolve_name_host_recv,
+ NULL);
+}
diff --git a/source4/libcli/resolve/nbtlist.c b/source4/libcli/resolve/nbtlist.c
new file mode 100644
index 0000000000..73085b87cb
--- /dev/null
+++ b/source4/libcli/resolve/nbtlist.c
@@ -0,0 +1,224 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ nbt list of addresses name resolution module
+
+ Copyright (C) Andrew Tridgell 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/>.
+*/
+
+/*
+ TODO: we should lower the timeout, and add retries for each name
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "system/network.h"
+#include "lib/socket/socket.h"
+#include "lib/socket/netif.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "../libcli/nbt/libnbt.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+struct nbtlist_state {
+ uint16_t flags;
+ uint16_t port;
+ struct nbt_name name;
+ struct nbt_name_socket *nbtsock;
+ int num_queries;
+ struct nbt_name_request **queries;
+ struct nbt_name_query *io_queries;
+ struct socket_address **addrs;
+ char **names;
+ struct interface *ifaces;
+};
+
+/*
+ handle events during nbtlist name resolution
+*/
+static void nbtlist_handler(struct nbt_name_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct nbtlist_state *state = talloc_get_type(c->private_data, struct nbtlist_state);
+ struct nbt_name_query *q;
+ int i;
+
+ for (i=0;i<state->num_queries;i++) {
+ if (req == state->queries[i]) break;
+ }
+
+ if (i == state->num_queries) {
+ /* not for us?! */
+ composite_error(c, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ q = &state->io_queries[i];
+
+ c->status = nbt_name_query_recv(req, state, q);
+
+ /* free the network resource directly */
+ talloc_free(state->nbtsock);
+ if (!composite_is_ok(c)) return;
+
+ if (q->out.num_addrs < 1) {
+ composite_error(c, NT_STATUS_UNEXPECTED_NETWORK_ERROR);
+ return;
+ }
+
+ state->addrs = talloc_array(state, struct socket_address *,
+ q->out.num_addrs + 1);
+ if (composite_nomem(state->addrs, c)) return;
+
+ state->names = talloc_array(state, char *, q->out.num_addrs + 1);
+ if (composite_nomem(state->names, c)) return;
+
+ for (i=0;i<q->out.num_addrs;i++) {
+ state->addrs[i] = socket_address_from_strings(state->addrs,
+ "ipv4",
+ q->out.reply_addrs[i],
+ state->port);
+ if (composite_nomem(state->addrs[i], c)) return;
+
+ state->names[i] = talloc_strdup(state->names, state->name.name);
+ if (composite_nomem(state->names[i], c)) return;
+ }
+ state->addrs[i] = NULL;
+ state->names[i] = NULL;
+
+ composite_done(c);
+}
+
+/*
+ nbtlist name resolution method - async send
+ */
+struct composite_context *resolve_name_nbtlist_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name,
+ const char **address_list,
+ struct interface *ifaces,
+ uint16_t nbt_port,
+ int nbt_timeout,
+ bool broadcast,
+ bool wins_lookup)
+{
+ struct composite_context *c;
+ struct nbtlist_state *state;
+ int i;
+
+ c = composite_create(mem_ctx, event_ctx);
+ if (c == NULL) return NULL;
+
+ if (flags & RESOLVE_NAME_FLAG_FORCE_DNS) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return c;
+ }
+
+ if (strlen(name->name) > 15) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return c;
+ }
+
+ state = talloc(c, struct nbtlist_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->flags = flags;
+ state->port = port;
+
+ c->status = nbt_name_dup(state, name, &state->name);
+ if (!composite_is_ok(c)) return c;
+
+ state->name.name = strupper_talloc(state, state->name.name);
+ if (composite_nomem(state->name.name, c)) return c;
+ if (state->name.scope) {
+ state->name.scope = strupper_talloc(state, state->name.scope);
+ if (composite_nomem(state->name.scope, c)) return c;
+ }
+
+ state->ifaces = talloc_reference(state, ifaces);
+
+ /*
+ * we can't push long names on the wire,
+ * so bail out here to give a useful error message
+ */
+ if (strlen(state->name.name) > 15) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return c;
+ }
+
+ state->nbtsock = nbt_name_socket_init(state, event_ctx,
+ global_iconv_convenience);
+ if (composite_nomem(state->nbtsock, c)) return c;
+
+ /* count the address_list size */
+ for (i=0;address_list[i];i++) /* noop */ ;
+
+ state->num_queries = i;
+ state->io_queries = talloc_array(state, struct nbt_name_query, state->num_queries);
+ if (composite_nomem(state->io_queries, c)) return c;
+
+ state->queries = talloc_array(state, struct nbt_name_request *, state->num_queries);
+ if (composite_nomem(state->queries, c)) return c;
+
+ for (i=0;i<state->num_queries;i++) {
+ state->io_queries[i].in.name = state->name;
+ state->io_queries[i].in.dest_addr = talloc_strdup(state->io_queries, address_list[i]);
+ state->io_queries[i].in.dest_port = nbt_port;
+ if (composite_nomem(state->io_queries[i].in.dest_addr, c)) return c;
+
+ state->io_queries[i].in.broadcast = broadcast;
+ state->io_queries[i].in.wins_lookup = wins_lookup;
+ state->io_queries[i].in.timeout = nbt_timeout;
+ state->io_queries[i].in.retries = 2;
+
+ state->queries[i] = nbt_name_query_send(state->nbtsock, &state->io_queries[i]);
+ if (composite_nomem(state->queries[i], c)) return c;
+
+ state->queries[i]->async.fn = nbtlist_handler;
+ state->queries[i]->async.private_data = c;
+ }
+
+ return c;
+}
+
+/*
+ nbt list of addresses name resolution method - recv side
+ */
+NTSTATUS resolve_name_nbtlist_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct nbtlist_state *state = talloc_get_type(c->private_data, struct nbtlist_state);
+ *addrs = talloc_steal(mem_ctx, state->addrs);
+ if (names) {
+ *names = talloc_steal(mem_ctx, state->names);
+ }
+ }
+
+ talloc_free(c);
+ return status;
+}
+
diff --git a/source4/libcli/resolve/resolve.c b/source4/libcli/resolve/resolve.c
new file mode 100644
index 0000000000..6a3d5daecc
--- /dev/null
+++ b/source4/libcli/resolve/resolve.c
@@ -0,0 +1,283 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ general name resolution interface
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Jelmer Vernooij 2007
+
+ 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/composite/composite.h"
+#include "libcli/resolve/resolve.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "system/network.h"
+#include "lib/socket/socket.h"
+#include "../lib/util/dlinklist.h"
+
+struct resolve_state {
+ struct resolve_context *ctx;
+ struct resolve_method *method;
+ uint32_t flags;
+ uint16_t port;
+ struct nbt_name name;
+ struct composite_context *creq;
+ struct socket_address **addrs;
+ char **names;
+};
+
+static struct composite_context *setup_next_method(struct composite_context *c);
+
+
+struct resolve_context {
+ struct resolve_method {
+ resolve_name_send_fn send_fn;
+ resolve_name_recv_fn recv_fn;
+ void *privdata;
+ struct resolve_method *prev, *next;
+ } *methods;
+};
+
+/**
+ * Initialize a resolve context
+ */
+struct resolve_context *resolve_context_init(TALLOC_CTX *mem_ctx)
+{
+ return talloc_zero(mem_ctx, struct resolve_context);
+}
+
+/**
+ * Add a resolve method
+ */
+bool resolve_context_add_method(struct resolve_context *ctx, resolve_name_send_fn send_fn,
+ resolve_name_recv_fn recv_fn, void *userdata)
+{
+ struct resolve_method *method = talloc_zero(ctx, struct resolve_method);
+
+ if (method == NULL)
+ return false;
+
+ method->send_fn = send_fn;
+ method->recv_fn = recv_fn;
+ method->privdata = userdata;
+ DLIST_ADD_END(ctx->methods, method, struct resolve_method *);
+ return true;
+}
+
+/**
+ handle completion of one name resolve method
+*/
+static void resolve_handler(struct composite_context *creq)
+{
+ struct composite_context *c = (struct composite_context *)creq->async.private_data;
+ struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state);
+ const struct resolve_method *method = state->method;
+
+ c->status = method->recv_fn(creq, state, &state->addrs, &state->names);
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ state->method = state->method->next;
+ state->creq = setup_next_method(c);
+ if (state->creq != NULL) {
+ return;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ } else {
+ c->state = COMPOSITE_STATE_DONE;
+ }
+ if (c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+
+static struct composite_context *setup_next_method(struct composite_context *c)
+{
+ struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state);
+ struct composite_context *creq = NULL;
+
+ do {
+ if (state->method) {
+ creq = state->method->send_fn(c, c->event_ctx,
+ state->method->privdata,
+ state->flags,
+ state->port,
+ &state->name);
+ }
+ if (creq == NULL && state->method) state->method = state->method->next;
+
+ } while (!creq && state->method);
+
+ if (creq) {
+ creq->async.fn = resolve_handler;
+ creq->async.private_data = c;
+ }
+
+ return creq;
+}
+
+/*
+ general name resolution - async send
+ */
+struct composite_context *resolve_name_all_send(struct resolve_context *ctx,
+ uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *c;
+ struct resolve_state *state;
+
+ if (ctx == NULL || event_ctx == NULL) {
+ return NULL;
+ }
+
+ c = composite_create(ctx, event_ctx);
+ if (c == NULL) return NULL;
+
+ if (composite_nomem(c->event_ctx, c)) return c;
+
+ state = talloc(c, struct resolve_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->flags = flags;
+ state->port = port;
+
+ c->status = nbt_name_dup(state, name, &state->name);
+ if (!composite_is_ok(c)) return c;
+
+ state->ctx = talloc_reference(state, ctx);
+ if (composite_nomem(state->ctx, c)) return c;
+
+ if (is_ipaddress(state->name.name) ||
+ strcasecmp(state->name.name, "localhost") == 0) {
+ struct in_addr ip = interpret_addr2(state->name.name);
+
+ state->addrs = talloc_array(state, struct socket_address *, 2);
+ if (composite_nomem(state->addrs, c)) return c;
+ state->addrs[0] = socket_address_from_strings(state->addrs, "ipv4",
+ inet_ntoa(ip), 0);
+ if (composite_nomem(state->addrs[0], c)) return c;
+ state->addrs[1] = NULL;
+ state->names = talloc_array(state, char *, 2);
+ if (composite_nomem(state->names, c)) return c;
+ state->names[0] = talloc_strdup(state->names, state->name.name);
+ if (composite_nomem(state->names[0], c)) return c;
+ state->names[1] = NULL;
+ composite_done(c);
+ return c;
+ }
+
+ state->method = ctx->methods;
+ if (state->method == NULL) {
+ composite_error(c, NT_STATUS_BAD_NETWORK_NAME);
+ return c;
+ }
+ state->creq = setup_next_method(c);
+ if (composite_nomem(state->creq, c)) return c;
+
+ return c;
+}
+
+/*
+ general name resolution method - recv side
+ */
+NTSTATUS resolve_name_all_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state);
+ *addrs = talloc_steal(mem_ctx, state->addrs);
+ if (names) {
+ *names = talloc_steal(mem_ctx, state->names);
+ }
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+struct composite_context *resolve_name_send(struct resolve_context *ctx,
+ struct nbt_name *name,
+ struct tevent_context *event_ctx)
+{
+ return resolve_name_all_send(ctx, 0, 0, name, event_ctx);
+}
+
+NTSTATUS resolve_name_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ const char **reply_addr)
+{
+ NTSTATUS status;
+ struct socket_address **addrs = NULL;
+
+ status = resolve_name_all_recv(c, mem_ctx, &addrs, NULL);
+
+ if (NT_STATUS_IS_OK(status)) {
+ *reply_addr = talloc_steal(mem_ctx, addrs[0]->addr);
+ talloc_free(addrs);
+ }
+
+ return status;
+}
+
+/*
+ general name resolution - sync call
+ */
+NTSTATUS resolve_name(struct resolve_context *ctx,
+ struct nbt_name *name,
+ TALLOC_CTX *mem_ctx,
+ const char **reply_addr,
+ struct tevent_context *ev)
+{
+ struct composite_context *c = resolve_name_send(ctx, name, ev);
+ return resolve_name_recv(c, mem_ctx, reply_addr);
+}
+
+/* Initialise a struct nbt_name with a NULL scope */
+
+void make_nbt_name(struct nbt_name *nbt, const char *name, int type)
+{
+ nbt->name = name;
+ nbt->scope = NULL;
+ nbt->type = type;
+}
+
+/* Initialise a struct nbt_name with a NBT_NAME_CLIENT (0x00) name */
+
+void make_nbt_name_client(struct nbt_name *nbt, const char *name)
+{
+ make_nbt_name(nbt, name, NBT_NAME_CLIENT);
+}
+
+/* Initialise a struct nbt_name with a NBT_NAME_SERVER (0x20) name */
+
+void make_nbt_name_server(struct nbt_name *nbt, const char *name)
+{
+ make_nbt_name(nbt, name, NBT_NAME_SERVER);
+}
+
+
diff --git a/source4/libcli/resolve/resolve.h b/source4/libcli/resolve/resolve.h
new file mode 100644
index 0000000000..8ace5740a9
--- /dev/null
+++ b/source4/libcli/resolve/resolve.h
@@ -0,0 +1,53 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ general name resolution interface
+
+ Copyright (C) Andrew Tridgell 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_RESOLVE_H__
+#define __LIBCLI_RESOLVE_H__
+
+struct socket_address;
+struct tevent_context;
+
+#include "../libcli/nbt/libnbt.h"
+
+/* force that only NBT name resolution is used */
+#define RESOLVE_NAME_FLAG_FORCE_NBT 0x00000001
+/* force that only DNS name resolution is used */
+#define RESOLVE_NAME_FLAG_FORCE_DNS 0x00000002
+/* tell the dns resolver to do a DNS SRV lookup */
+#define RESOLVE_NAME_FLAG_DNS_SRV 0x00000004
+/* allow the resolver to overwrite the given port, e.g. for DNS SRV */
+#define RESOLVE_NAME_FLAG_OVERWRITE_PORT 0x00000008
+
+typedef struct composite_context *(*resolve_name_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *,
+ void *privdata,
+ uint32_t flags,
+ uint16_t port,
+ struct nbt_name *);
+typedef NTSTATUS (*resolve_name_recv_fn)(struct composite_context *creq,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names);
+#include "libcli/resolve/proto.h"
+struct interface;
+#include "libcli/resolve/lp_proto.h"
+
+#endif /* __LIBCLI_RESOLVE_H__ */
diff --git a/source4/libcli/resolve/resolve_lp.c b/source4/libcli/resolve/resolve_lp.c
new file mode 100644
index 0000000000..b41e2b98d8
--- /dev/null
+++ b/source4/libcli/resolve/resolve_lp.c
@@ -0,0 +1,46 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+
+ 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/resolve/resolve.h"
+#include "param/param.h"
+
+struct resolve_context *lp_resolve_context(struct loadparm_context *lp_ctx)
+{
+ const char **methods = lp_name_resolve_order(lp_ctx);
+ int i;
+ struct resolve_context *ret = resolve_context_init(lp_ctx);
+
+ if (ret == NULL)
+ return NULL;
+
+ for (i = 0; methods != NULL && methods[i] != NULL; i++) {
+ if (!strcmp(methods[i], "wins")) {
+ resolve_context_add_wins_method_lp(ret, lp_ctx);
+ } else if (!strcmp(methods[i], "bcast")) {
+ resolve_context_add_bcast_method_lp(ret, lp_ctx);
+ } else if (!strcmp(methods[i], "host")) {
+ resolve_context_add_host_method(ret);
+ } else {
+ DEBUG(0, ("Unknown resolve method '%s'\n", methods[i]));
+ }
+ }
+
+ return ret;
+}
diff --git a/source4/libcli/resolve/testsuite.c b/source4/libcli/resolve/testsuite.c
new file mode 100644
index 0000000000..fdbb430a9f
--- /dev/null
+++ b/source4/libcli/resolve/testsuite.c
@@ -0,0 +1,90 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local test for async resolve code
+
+ 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 "lib/events/events.h"
+#include "libcli/resolve/resolve.h"
+#include "torture/torture.h"
+#include "system/network.h"
+
+static bool test_async_resolve(struct torture_context *tctx)
+{
+ struct nbt_name n;
+ struct tevent_context *ev;
+ int timelimit = torture_setting_int(tctx, "timelimit", 2);
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ int count = 0;
+ struct timeval tv = timeval_current();
+ TALLOC_CTX *mem_ctx = tctx;
+
+ ev = tctx->ev;
+
+ ZERO_STRUCT(n);
+ n.name = host;
+
+ torture_comment(tctx, "Testing async resolve of '%s' for %d seconds\n",
+ host, timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ struct socket_address **s;
+ struct composite_context *c = resolve_name_host_send(mem_ctx, ev, NULL, 0, 0, &n);
+ torture_assert(tctx, c != NULL, "resolve_name_host_send");
+ torture_assert_ntstatus_ok(tctx, resolve_name_host_recv(c, mem_ctx, &s, NULL),
+ "async resolve failed");
+ count++;
+ }
+
+ torture_comment(tctx, "async rate of %.1f resolves/sec\n",
+ count/timeval_elapsed(&tv));
+ return true;
+}
+
+/*
+ test resolution using sync method
+*/
+static bool test_sync_resolve(struct torture_context *tctx)
+{
+ int timelimit = torture_setting_int(tctx, "timelimit", 2);
+ struct timeval tv = timeval_current();
+ int count = 0;
+ const char *host = torture_setting_string(tctx, "host", NULL);
+
+ torture_comment(tctx, "Testing sync resolve of '%s' for %d seconds\n",
+ host, timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ inet_ntoa(interpret_addr2(host));
+ count++;
+ }
+
+ torture_comment(tctx, "sync rate of %.1f resolves/sec\n",
+ count/timeval_elapsed(&tv));
+ return true;
+}
+
+
+struct torture_suite *torture_local_resolve(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "RESOLVE");
+
+ torture_suite_add_simple_test(suite, "async", test_async_resolve);
+ torture_suite_add_simple_test(suite, "sync", test_sync_resolve);
+
+ return suite;
+}
diff --git a/source4/libcli/resolve/wins.c b/source4/libcli/resolve/wins.c
new file mode 100644
index 0000000000..03eaf8786f
--- /dev/null
+++ b/source4/libcli/resolve/wins.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ wins name resolution module
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "../libcli/nbt/libnbt.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+#include "lib/socket/socket.h"
+#include "lib/socket/netif.h"
+
+struct resolve_wins_data {
+ const char **address_list;
+ struct interface *ifaces;
+ uint16_t nbt_port;
+ int nbt_timeout;
+};
+
+/**
+ wins name resolution method - async send
+ */
+struct composite_context *resolve_name_wins_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ void *userdata,
+ uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name)
+{
+ struct resolve_wins_data *wins_data = talloc_get_type(userdata, struct resolve_wins_data);
+ if (wins_data->address_list == NULL) return NULL;
+ return resolve_name_nbtlist_send(mem_ctx, event_ctx, flags, port, name,
+ wins_data->address_list, wins_data->ifaces,
+ wins_data->nbt_port, wins_data->nbt_timeout,
+ false, true);
+}
+
+/*
+ wins name resolution method - recv side
+ */
+NTSTATUS resolve_name_wins_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names)
+{
+ return resolve_name_nbtlist_recv(c, mem_ctx, addrs, names);
+}
+
+bool resolve_context_add_wins_method(struct resolve_context *ctx, const char **address_list, struct interface *ifaces, uint16_t nbt_port, int nbt_timeout)
+{
+ struct resolve_wins_data *wins_data = talloc(ctx, struct resolve_wins_data);
+ wins_data->address_list = (const char **)str_list_copy(wins_data, address_list);
+ wins_data->ifaces = talloc_reference(wins_data, ifaces);
+ wins_data->nbt_port = nbt_port;
+ wins_data->nbt_timeout = nbt_timeout;
+ return resolve_context_add_method(ctx, resolve_name_wins_send, resolve_name_wins_recv,
+ wins_data);
+}
+
+bool resolve_context_add_wins_method_lp(struct resolve_context *ctx, struct loadparm_context *lp_ctx)
+{
+ struct interface *ifaces;
+ load_interfaces(ctx, lp_interfaces(lp_ctx), &ifaces);
+ return resolve_context_add_wins_method(ctx, lp_wins_server_list(lp_ctx), ifaces, lp_nbt_port(lp_ctx), lp_parm_int(lp_ctx, NULL, "nbt", "timeout", 1));
+}
diff --git a/source4/libcli/security/access_check.c b/source4/libcli/security/access_check.c
new file mode 100644
index 0000000000..af6a3d6fb3
--- /dev/null
+++ b/source4/libcli/security/access_check.c
@@ -0,0 +1,155 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security access checking routines
+
+ 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/security/security.h"
+
+
+/*
+ perform a SEC_FLAG_MAXIMUM_ALLOWED access check
+*/
+static uint32_t access_check_max_allowed(const struct security_descriptor *sd,
+ const struct security_token *token)
+{
+ uint32_t denied = 0, granted = 0;
+ unsigned i;
+
+ if (security_token_has_sid(token, sd->owner_sid)) {
+ granted |= SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL | SEC_STD_DELETE;
+ } else if (security_token_has_privilege(token, SEC_PRIV_RESTORE)) {
+ granted |= SEC_STD_DELETE;
+ }
+
+ if (sd->dacl == NULL) {
+ return granted & ~denied;
+ }
+
+ for (i = 0;i<sd->dacl->num_aces; i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ continue;
+ }
+
+ if (!security_token_has_sid(token, &ace->trustee)) {
+ continue;
+ }
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ granted |= ace->access_mask;
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ denied |= ace->access_mask;
+ break;
+ default: /* Other ACE types not handled/supported */
+ break;
+ }
+ }
+
+ return granted & ~denied;
+}
+
+/*
+ the main entry point for access checking.
+*/
+NTSTATUS sec_access_check(const struct security_descriptor *sd,
+ const struct security_token *token,
+ uint32_t access_desired,
+ uint32_t *access_granted)
+{
+ int i;
+ uint32_t bits_remaining;
+
+ *access_granted = access_desired;
+ bits_remaining = access_desired;
+
+ /* handle the maximum allowed flag */
+ if (access_desired & SEC_FLAG_MAXIMUM_ALLOWED) {
+ access_desired |= access_check_max_allowed(sd, token);
+ access_desired &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+ *access_granted = access_desired;
+ bits_remaining = access_desired & ~SEC_STD_DELETE;
+ }
+
+ if (access_desired & SEC_FLAG_SYSTEM_SECURITY) {
+ if (security_token_has_privilege(token, SEC_PRIV_SECURITY)) {
+ bits_remaining &= ~SEC_FLAG_SYSTEM_SECURITY;
+ } else {
+ return NT_STATUS_PRIVILEGE_NOT_HELD;
+ }
+ }
+
+ /* a NULL dacl allows access */
+ if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl == NULL) {
+ *access_granted = access_desired;
+ return NT_STATUS_OK;
+ }
+
+ /* the owner always gets SEC_STD_WRITE_DAC, SEC_STD_READ_CONTROL and SEC_STD_DELETE */
+ if ((bits_remaining & (SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL|SEC_STD_DELETE)) &&
+ security_token_has_sid(token, sd->owner_sid)) {
+ bits_remaining &= ~(SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL|SEC_STD_DELETE);
+ }
+ if ((bits_remaining & SEC_STD_DELETE) &&
+ security_token_has_privilege(token, SEC_PRIV_RESTORE)) {
+ bits_remaining &= ~SEC_STD_DELETE;
+ }
+
+ if (sd->dacl == NULL) {
+ goto done;
+ }
+
+ /* check each ace in turn. */
+ for (i=0; bits_remaining && i < sd->dacl->num_aces; i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ continue;
+ }
+
+ if (!security_token_has_sid(token, &ace->trustee)) {
+ continue;
+ }
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ bits_remaining &= ~ace->access_mask;
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ if (bits_remaining & ace->access_mask) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ default: /* Other ACE types not handled/supported */
+ break;
+ }
+ }
+
+done:
+ if (bits_remaining != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/security/config.mk b/source4/libcli/security/config.mk
new file mode 100644
index 0000000000..d6d9ad5545
--- /dev/null
+++ b/source4/libcli/security/config.mk
@@ -0,0 +1,10 @@
+[SUBSYSTEM::LIBSECURITY]
+PUBLIC_DEPENDENCIES = LIBNDR LIBSECURITY_COMMON
+
+LIBSECURITY_OBJ_FILES = $(addprefix $(libclisrcdir)/security/, \
+ security_token.o security_descriptor.o \
+ access_check.o privilege.o sddl.o) \
+ ../libcli/security/secace.o \
+ ../libcli/security/secacl.o
+
+$(eval $(call proto_header_template,$(libclisrcdir)/security/proto.h,$(LIBSECURITY_OBJ_FILES:.o=.c)))
diff --git a/source4/libcli/security/privilege.c b/source4/libcli/security/privilege.c
new file mode 100644
index 0000000000..2cbef13538
--- /dev/null
+++ b/source4/libcli/security/privilege.c
@@ -0,0 +1,241 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ manipulate privileges
+
+ 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 "librpc/gen_ndr/security.h"
+#include "libcli/security/security.h"
+
+
+static const struct {
+ enum sec_privilege privilege;
+ const char *name;
+ const char *display_name;
+} privilege_names[] = {
+ {SEC_PRIV_SECURITY,
+ "SeSecurityPrivilege",
+ "System security"},
+
+ {SEC_PRIV_BACKUP,
+ "SeBackupPrivilege",
+ "Backup files and directories"},
+
+ {SEC_PRIV_RESTORE,
+ "SeRestorePrivilege",
+ "Restore files and directories"},
+
+ {SEC_PRIV_SYSTEMTIME,
+ "SeSystemtimePrivilege",
+ "Set the system clock"},
+
+ {SEC_PRIV_SHUTDOWN,
+ "SeShutdownPrivilege",
+ "Shutdown the system"},
+
+ {SEC_PRIV_REMOTE_SHUTDOWN,
+ "SeRemoteShutdownPrivilege",
+ "Shutdown the system remotely"},
+
+ {SEC_PRIV_TAKE_OWNERSHIP,
+ "SeTakeOwnershipPrivilege",
+ "Take ownership of files and directories"},
+
+ {SEC_PRIV_DEBUG,
+ "SeDebugPrivilege",
+ "Debug processes"},
+
+ {SEC_PRIV_SYSTEM_ENVIRONMENT,
+ "SeSystemEnvironmentPrivilege",
+ "Modify system environment"},
+
+ {SEC_PRIV_SYSTEM_PROFILE,
+ "SeSystemProfilePrivilege",
+ "Profile the system"},
+
+ {SEC_PRIV_PROFILE_SINGLE_PROCESS,
+ "SeProfileSingleProcessPrivilege",
+ "Profile one process"},
+
+ {SEC_PRIV_INCREASE_BASE_PRIORITY,
+ "SeIncreaseBasePriorityPrivilege",
+ "Increase base priority"},
+
+ {SEC_PRIV_LOAD_DRIVER,
+ "SeLoadDriverPrivilege",
+ "Load drivers"},
+
+ {SEC_PRIV_CREATE_PAGEFILE,
+ "SeCreatePagefilePrivilege",
+ "Create page files"},
+
+ {SEC_PRIV_INCREASE_QUOTA,
+ "SeIncreaseQuotaPrivilege",
+ "Increase quota"},
+
+ {SEC_PRIV_CHANGE_NOTIFY,
+ "SeChangeNotifyPrivilege",
+ "Register for change notify"},
+
+ {SEC_PRIV_UNDOCK,
+ "SeUndockPrivilege",
+ "Undock devices"},
+
+ {SEC_PRIV_MANAGE_VOLUME,
+ "SeManageVolumePrivilege",
+ "Manage system volumes"},
+
+ {SEC_PRIV_IMPERSONATE,
+ "SeImpersonatePrivilege",
+ "Impersonate users"},
+
+ {SEC_PRIV_CREATE_GLOBAL,
+ "SeCreateGlobalPrivilege",
+ "Create global"},
+
+ {SEC_PRIV_ENABLE_DELEGATION,
+ "SeEnableDelegationPrivilege",
+ "Enable Delegation"},
+
+ {SEC_PRIV_INTERACTIVE_LOGON,
+ "SeInteractiveLogonRight",
+ "Interactive logon"},
+
+ {SEC_PRIV_NETWORK_LOGON,
+ "SeNetworkLogonRight",
+ "Network logon"},
+
+ {SEC_PRIV_REMOTE_INTERACTIVE_LOGON,
+ "SeRemoteInteractiveLogonRight",
+ "Remote Interactive logon"}
+};
+
+
+/*
+ map a privilege id to the wire string constant
+*/
+const char *sec_privilege_name(enum sec_privilege privilege)
+{
+ int i;
+ for (i=0;i<ARRAY_SIZE(privilege_names);i++) {
+ if (privilege_names[i].privilege == privilege) {
+ return privilege_names[i].name;
+ }
+ }
+ return NULL;
+}
+
+/*
+ map a privilege id to a privilege display name. Return NULL if not found
+
+ TODO: this should use language mappings
+*/
+const char *sec_privilege_display_name(enum sec_privilege privilege, uint16_t *language)
+{
+ int i;
+ if (privilege < 1 || privilege > 64) {
+ return NULL;
+ }
+ for (i=0;i<ARRAY_SIZE(privilege_names);i++) {
+ if (privilege_names[i].privilege == privilege) {
+ return privilege_names[i].display_name;
+ }
+ }
+ return NULL;
+}
+
+/*
+ map a privilege name to a privilege id. Return -1 if not found
+*/
+enum sec_privilege sec_privilege_id(const char *name)
+{
+ int i;
+ for (i=0;i<ARRAY_SIZE(privilege_names);i++) {
+ if (strcasecmp(privilege_names[i].name, name) == 0) {
+ return privilege_names[i].privilege;
+ }
+ }
+ return -1;
+}
+
+
+/*
+ return a privilege mask given a privilege id
+*/
+static uint64_t sec_privilege_mask(enum sec_privilege privilege)
+{
+ uint64_t mask = 1;
+
+ if (privilege < 1 || privilege > 64) {
+ return 0;
+ }
+
+ mask <<= (privilege-1);
+ return mask;
+}
+
+
+/*
+ return true if a security_token has a particular privilege bit set
+*/
+bool security_token_has_privilege(const struct security_token *token, enum sec_privilege privilege)
+{
+ uint64_t mask;
+
+ if (privilege < 1 || privilege > 64) {
+ return false;
+ }
+
+ mask = sec_privilege_mask(privilege);
+ if (token->privilege_mask & mask) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ set a bit in the privilege mask
+*/
+void security_token_set_privilege(struct security_token *token, enum sec_privilege privilege)
+{
+ if (privilege < 1 || privilege > 64) {
+ return;
+ }
+ token->privilege_mask |= sec_privilege_mask(privilege);
+}
+
+void security_token_debug_privileges(int dbg_lev, const struct security_token *token)
+{
+ DEBUGADD(dbg_lev, (" Privileges (0x%16llX):\n",
+ (unsigned long long) token->privilege_mask));
+
+ if (token->privilege_mask) {
+ int i = 0;
+ uint_t privilege;
+
+ for (privilege = 1; privilege <= 64; privilege++) {
+ uint64_t mask = sec_privilege_mask(privilege);
+
+ if (token->privilege_mask & mask) {
+ DEBUGADD(dbg_lev, (" Privilege[%3lu]: %s\n", (unsigned long)i++,
+ sec_privilege_name(privilege)));
+ }
+ }
+ }
+}
diff --git a/source4/libcli/security/sddl.c b/source4/libcli/security/sddl.c
new file mode 100644
index 0000000000..a8d893f085
--- /dev/null
+++ b/source4/libcli/security/sddl.c
@@ -0,0 +1,598 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security descriptor description language functions
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "system/locale.h"
+
+struct flag_map {
+ const char *name;
+ uint32_t flag;
+};
+
+/*
+ map a series of letter codes into a uint32_t
+*/
+static bool sddl_map_flags(const struct flag_map *map, const char *str,
+ uint32_t *flags, size_t *len)
+{
+ const char *str0 = str;
+ if (len) *len = 0;
+ *flags = 0;
+ while (str[0] && isupper(str[0])) {
+ int i;
+ for (i=0;map[i].name;i++) {
+ size_t l = strlen(map[i].name);
+ if (strncmp(map[i].name, str, l) == 0) {
+ *flags |= map[i].flag;
+ str += l;
+ if (len) *len += l;
+ break;
+ }
+ }
+ if (map[i].name == NULL) {
+ DEBUG(1, ("Unknown flag - %s in %s\n", str, str0));
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ a mapping between the 2 letter SID codes and sid strings
+*/
+static const struct {
+ const char *code;
+ const char *sid;
+ uint32_t rid;
+} sid_codes[] = {
+ { "AO", SID_BUILTIN_ACCOUNT_OPERATORS },
+ { "BA", SID_BUILTIN_ADMINISTRATORS },
+ { "RU", SID_BUILTIN_PREW2K },
+ { "PO", SID_BUILTIN_PRINT_OPERATORS },
+ { "RS", SID_BUILTIN_RAS_SERVERS },
+
+ { "AU", SID_NT_AUTHENTICATED_USERS },
+ { "SY", SID_NT_SYSTEM },
+ { "PS", SID_NT_SELF },
+ { "WD", SID_WORLD },
+ { "ED", SID_NT_ENTERPRISE_DCS },
+
+ { "CO", SID_CREATOR_OWNER },
+ { "CG", SID_CREATOR_GROUP },
+
+ { "DA", NULL, DOMAIN_RID_ADMINS },
+ { "EA", NULL, DOMAIN_RID_ENTERPRISE_ADMINS },
+ { "DD", NULL, DOMAIN_RID_DCS },
+ { "DU", NULL, DOMAIN_RID_USERS },
+ { "CA", NULL, DOMAIN_RID_CERT_ADMINS },
+};
+
+/*
+ decode a SID
+ It can either be a special 2 letter code, or in S-* format
+*/
+static struct dom_sid *sddl_decode_sid(TALLOC_CTX *mem_ctx, const char **sddlp,
+ const struct dom_sid *domain_sid)
+{
+ const char *sddl = (*sddlp);
+ int i;
+
+ /* see if its in the numeric format */
+ if (strncmp(sddl, "S-", 2) == 0) {
+ struct dom_sid *sid;
+ char *sid_str;
+ size_t len = strspn(sddl+2, "-0123456789");
+ sid_str = talloc_strndup(mem_ctx, sddl, len+2);
+ if (!sid_str) {
+ return NULL;
+ }
+ (*sddlp) += len+2;
+ sid = dom_sid_parse_talloc(mem_ctx, sid_str);
+ talloc_free(sid_str);
+ return sid;
+ }
+
+ /* now check for one of the special codes */
+ for (i=0;i<ARRAY_SIZE(sid_codes);i++) {
+ if (strncmp(sid_codes[i].code, sddl, 2) == 0) break;
+ }
+ if (i == ARRAY_SIZE(sid_codes)) {
+ DEBUG(1,("Unknown sddl sid code '%2.2s'\n", sddl));
+ return NULL;
+ }
+
+ (*sddlp) += 2;
+
+ if (sid_codes[i].sid == NULL) {
+ return dom_sid_add_rid(mem_ctx, domain_sid, sid_codes[i].rid);
+ }
+
+ return dom_sid_parse_talloc(mem_ctx, sid_codes[i].sid);
+}
+
+static const struct flag_map ace_types[] = {
+ { "AU", SEC_ACE_TYPE_SYSTEM_AUDIT },
+ { "AL", SEC_ACE_TYPE_SYSTEM_ALARM },
+ { "OA", SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT },
+ { "OD", SEC_ACE_TYPE_ACCESS_DENIED_OBJECT },
+ { "OU", SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT },
+ { "OL", SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT },
+ { "A", SEC_ACE_TYPE_ACCESS_ALLOWED },
+ { "D", SEC_ACE_TYPE_ACCESS_DENIED },
+ { NULL, 0 }
+};
+
+static const struct flag_map ace_flags[] = {
+ { "OI", SEC_ACE_FLAG_OBJECT_INHERIT },
+ { "CI", SEC_ACE_FLAG_CONTAINER_INHERIT },
+ { "NP", SEC_ACE_FLAG_NO_PROPAGATE_INHERIT },
+ { "IO", SEC_ACE_FLAG_INHERIT_ONLY },
+ { "ID", SEC_ACE_FLAG_INHERITED_ACE },
+ { "SA", SEC_ACE_FLAG_SUCCESSFUL_ACCESS },
+ { "FA", SEC_ACE_FLAG_FAILED_ACCESS },
+ { NULL, 0 },
+};
+
+static const struct flag_map ace_access_mask[] = {
+ { "RP", SEC_ADS_READ_PROP },
+ { "WP", SEC_ADS_WRITE_PROP },
+ { "CR", SEC_ADS_CONTROL_ACCESS },
+ { "CC", SEC_ADS_CREATE_CHILD },
+ { "DC", SEC_ADS_DELETE_CHILD },
+ { "LC", SEC_ADS_LIST },
+ { "LO", SEC_ADS_LIST_OBJECT },
+ { "RC", SEC_STD_READ_CONTROL },
+ { "WO", SEC_STD_WRITE_OWNER },
+ { "WD", SEC_STD_WRITE_DAC },
+ { "SD", SEC_STD_DELETE },
+ { "DT", SEC_ADS_DELETE_TREE },
+ { "SW", SEC_ADS_SELF_WRITE },
+ { "GA", SEC_GENERIC_ALL },
+ { "GR", SEC_GENERIC_READ },
+ { "GW", SEC_GENERIC_WRITE },
+ { "GX", SEC_GENERIC_EXECUTE },
+ { NULL, 0 }
+};
+
+/*
+ decode an ACE
+ return true on success, false on failure
+ note that this routine modifies the string
+*/
+static bool sddl_decode_ace(TALLOC_CTX *mem_ctx, struct security_ace *ace, char *str,
+ const struct dom_sid *domain_sid)
+{
+ const char *tok[6];
+ const char *s;
+ int i;
+ uint32_t v;
+ struct dom_sid *sid;
+
+ ZERO_STRUCTP(ace);
+
+ /* parse out the 6 tokens */
+ tok[0] = str;
+ for (i=0;i<5;i++) {
+ char *ptr = strchr(str, ';');
+ if (ptr == NULL) return false;
+ *ptr = 0;
+ str = ptr+1;
+ tok[i+1] = str;
+ }
+
+ /* parse ace type */
+ if (!sddl_map_flags(ace_types, tok[0], &v, NULL)) {
+ return false;
+ }
+ ace->type = v;
+
+ /* ace flags */
+ if (!sddl_map_flags(ace_flags, tok[1], &v, NULL)) {
+ return false;
+ }
+ ace->flags = v;
+
+ /* access mask */
+ if (strncmp(tok[2], "0x", 2) == 0) {
+ ace->access_mask = strtol(tok[2], NULL, 16);
+ } else {
+ if (!sddl_map_flags(ace_access_mask, tok[2], &v, NULL)) {
+ return false;
+ }
+ ace->access_mask = v;
+ }
+
+ /* object */
+ if (tok[3][0] != 0) {
+ NTSTATUS status = GUID_from_string(tok[3],
+ &ace->object.object.type.type);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ ace->object.object.flags |= SEC_ACE_OBJECT_TYPE_PRESENT;
+ }
+
+ /* inherit object */
+ if (tok[4][0] != 0) {
+ NTSTATUS status = GUID_from_string(tok[4],
+ &ace->object.object.inherited_type.inherited_type);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ ace->object.object.flags |= SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT;
+ }
+
+ /* trustee */
+ s = tok[5];
+ sid = sddl_decode_sid(mem_ctx, &s, domain_sid);
+ if (sid == NULL) {
+ return false;
+ }
+ ace->trustee = *sid;
+ talloc_free(sid);
+
+ return true;
+}
+
+static const struct flag_map acl_flags[] = {
+ { "P", SEC_DESC_DACL_PROTECTED },
+ { "AR", SEC_DESC_DACL_AUTO_INHERIT_REQ },
+ { "AI", SEC_DESC_DACL_AUTO_INHERITED },
+ { NULL, 0 }
+};
+
+/*
+ decode an ACL
+*/
+static struct security_acl *sddl_decode_acl(struct security_descriptor *sd,
+ const char **sddlp, uint32_t *flags,
+ const struct dom_sid *domain_sid)
+{
+ const char *sddl = *sddlp;
+ struct security_acl *acl;
+ size_t len;
+
+ *flags = 0;
+
+ acl = talloc_zero(sd, struct security_acl);
+ if (acl == NULL) return NULL;
+ acl->revision = SECURITY_ACL_REVISION_NT4;
+
+ if (isupper(sddl[0]) && sddl[1] == ':') {
+ /* its an empty ACL */
+ return acl;
+ }
+
+ /* work out the ACL flags */
+ if (!sddl_map_flags(acl_flags, sddl, flags, &len)) {
+ talloc_free(acl);
+ return NULL;
+ }
+ sddl += len;
+
+ /* now the ACEs */
+ while (*sddl == '(') {
+ char *astr;
+ len = strcspn(sddl+1, ")");
+ astr = talloc_strndup(acl, sddl+1, len);
+ if (astr == NULL || sddl[len+1] != ')') {
+ talloc_free(acl);
+ return NULL;
+ }
+ acl->aces = talloc_realloc(acl, acl->aces, struct security_ace,
+ acl->num_aces+1);
+ if (acl->aces == NULL) {
+ talloc_free(acl);
+ return NULL;
+ }
+ if (!sddl_decode_ace(acl->aces, &acl->aces[acl->num_aces],
+ astr, domain_sid)) {
+ talloc_free(acl);
+ return NULL;
+ }
+ switch (acl->aces[acl->num_aces].type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
+ acl->revision = SECURITY_ACL_REVISION_ADS;
+ break;
+ default:
+ break;
+ }
+ talloc_free(astr);
+ sddl += len+2;
+ acl->num_aces++;
+ }
+
+ (*sddlp) = sddl;
+ return acl;
+}
+
+/*
+ decode a security descriptor in SDDL format
+*/
+struct security_descriptor *sddl_decode(TALLOC_CTX *mem_ctx, const char *sddl,
+ const struct dom_sid *domain_sid)
+{
+ struct security_descriptor *sd;
+ sd = talloc_zero(mem_ctx, struct security_descriptor);
+
+ sd->revision = SECURITY_DESCRIPTOR_REVISION_1;
+ sd->type = SEC_DESC_SELF_RELATIVE;
+
+ while (*sddl) {
+ uint32_t flags;
+ char c = sddl[0];
+ if (sddl[1] != ':') goto failed;
+
+ sddl += 2;
+ switch (c) {
+ case 'D':
+ if (sd->dacl != NULL) goto failed;
+ sd->dacl = sddl_decode_acl(sd, &sddl, &flags, domain_sid);
+ if (sd->dacl == NULL) goto failed;
+ sd->type |= flags | SEC_DESC_DACL_PRESENT;
+ break;
+ case 'S':
+ if (sd->sacl != NULL) goto failed;
+ sd->sacl = sddl_decode_acl(sd, &sddl, &flags, domain_sid);
+ if (sd->sacl == NULL) goto failed;
+ /* this relies on the SEC_DESC_SACL_* flags being
+ 1 bit shifted from the SEC_DESC_DACL_* flags */
+ sd->type |= (flags<<1) | SEC_DESC_SACL_PRESENT;
+ break;
+ case 'O':
+ if (sd->owner_sid != NULL) goto failed;
+ sd->owner_sid = sddl_decode_sid(sd, &sddl, domain_sid);
+ if (sd->owner_sid == NULL) goto failed;
+ break;
+ case 'G':
+ if (sd->group_sid != NULL) goto failed;
+ sd->group_sid = sddl_decode_sid(sd, &sddl, domain_sid);
+ if (sd->group_sid == NULL) goto failed;
+ break;
+ }
+ }
+
+ return sd;
+
+failed:
+ DEBUG(2,("Badly formatted SDDL '%s'\n", sddl));
+ talloc_free(sd);
+ return NULL;
+}
+
+/*
+ turn a set of flags into a string
+*/
+static char *sddl_flags_to_string(TALLOC_CTX *mem_ctx, const struct flag_map *map,
+ uint32_t flags, bool check_all)
+{
+ int i;
+ char *s;
+
+ /* try to find an exact match */
+ for (i=0;map[i].name;i++) {
+ if (map[i].flag == flags) {
+ return talloc_strdup(mem_ctx, map[i].name);
+ }
+ }
+
+ s = talloc_strdup(mem_ctx, "");
+
+ /* now by bits */
+ for (i=0;map[i].name;i++) {
+ if ((flags & map[i].flag) != 0) {
+ s = talloc_asprintf_append_buffer(s, "%s", map[i].name);
+ if (s == NULL) goto failed;
+ flags &= ~map[i].flag;
+ }
+ }
+
+ if (check_all && flags != 0) {
+ goto failed;
+ }
+
+ return s;
+
+failed:
+ talloc_free(s);
+ return NULL;
+}
+
+/*
+ encode a sid in SDDL format
+*/
+static char *sddl_encode_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ const struct dom_sid *domain_sid)
+{
+ int i;
+ char *sidstr;
+
+ sidstr = dom_sid_string(mem_ctx, sid);
+ if (sidstr == NULL) return NULL;
+
+ /* seen if its a well known sid */
+ for (i=0;sid_codes[i].sid;i++) {
+ if (strcmp(sidstr, sid_codes[i].sid) == 0) {
+ talloc_free(sidstr);
+ return talloc_strdup(mem_ctx, sid_codes[i].code);
+ }
+ }
+
+ /* or a well known rid in our domain */
+ if (dom_sid_in_domain(domain_sid, sid)) {
+ uint32_t rid = sid->sub_auths[sid->num_auths-1];
+ for (;i<ARRAY_SIZE(sid_codes);i++) {
+ if (rid == sid_codes[i].rid) {
+ talloc_free(sidstr);
+ return talloc_strdup(mem_ctx, sid_codes[i].code);
+ }
+ }
+ }
+
+ talloc_free(sidstr);
+
+ /* TODO: encode well known sids as two letter codes */
+ return dom_sid_string(mem_ctx, sid);
+}
+
+
+/*
+ encode an ACE in SDDL format
+*/
+static char *sddl_encode_ace(TALLOC_CTX *mem_ctx, const struct security_ace *ace,
+ const struct dom_sid *domain_sid)
+{
+ char *sddl = NULL;
+ TALLOC_CTX *tmp_ctx;
+ const char *s_type="", *s_flags="", *s_mask="",
+ *s_object="", *s_iobject="", *s_trustee="";
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return NULL;
+ }
+
+ s_type = sddl_flags_to_string(tmp_ctx, ace_types, ace->type, true);
+ if (s_type == NULL) goto failed;
+
+ s_flags = sddl_flags_to_string(tmp_ctx, ace_flags, ace->flags, true);
+ if (s_flags == NULL) goto failed;
+
+ s_mask = sddl_flags_to_string(tmp_ctx, ace_access_mask, ace->access_mask, true);
+ if (s_mask == NULL) {
+ s_mask = talloc_asprintf(tmp_ctx, "0x%08x", ace->access_mask);
+ if (s_mask == NULL) goto failed;
+ }
+
+ if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+ ace->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+ ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+ ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT) {
+ if (ace->object.object.flags & SEC_ACE_OBJECT_TYPE_PRESENT) {
+ s_object = GUID_string(tmp_ctx, &ace->object.object.type.type);
+ if (s_object == NULL) goto failed;
+ }
+
+ if (ace->object.object.flags & SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT) {
+ s_iobject = GUID_string(tmp_ctx, &ace->object.object.inherited_type.inherited_type);
+ if (s_iobject == NULL) goto failed;
+ }
+ }
+
+ s_trustee = sddl_encode_sid(tmp_ctx, &ace->trustee, domain_sid);
+ if (s_trustee == NULL) goto failed;
+
+ sddl = talloc_asprintf(mem_ctx, "%s;%s;%s;%s;%s;%s",
+ s_type, s_flags, s_mask, s_object, s_iobject, s_trustee);
+
+failed:
+ talloc_free(tmp_ctx);
+ return sddl;
+}
+
+/*
+ encode an ACL in SDDL format
+*/
+static char *sddl_encode_acl(TALLOC_CTX *mem_ctx, const struct security_acl *acl,
+ uint32_t flags, const struct dom_sid *domain_sid)
+{
+ char *sddl;
+ int i;
+
+ /* add any ACL flags */
+ sddl = sddl_flags_to_string(mem_ctx, acl_flags, flags, false);
+ if (sddl == NULL) goto failed;
+
+ /* now the ACEs, encoded in braces */
+ for (i=0;i<acl->num_aces;i++) {
+ char *ace = sddl_encode_ace(sddl, &acl->aces[i], domain_sid);
+ if (ace == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "(%s)", ace);
+ if (sddl == NULL) goto failed;
+ talloc_free(ace);
+ }
+
+ return sddl;
+
+failed:
+ talloc_free(sddl);
+ return NULL;
+}
+
+
+/*
+ encode a security descriptor to SDDL format
+*/
+char *sddl_encode(TALLOC_CTX *mem_ctx, const struct security_descriptor *sd,
+ const struct dom_sid *domain_sid)
+{
+ char *sddl;
+ TALLOC_CTX *tmp_ctx;
+
+ /* start with a blank string */
+ sddl = talloc_strdup(mem_ctx, "");
+ if (sddl == NULL) goto failed;
+
+ tmp_ctx = talloc_new(mem_ctx);
+
+ if (sd->owner_sid != NULL) {
+ char *sid = sddl_encode_sid(tmp_ctx, sd->owner_sid, domain_sid);
+ if (sid == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "O:%s", sid);
+ if (sddl == NULL) goto failed;
+ }
+
+ if (sd->group_sid != NULL) {
+ char *sid = sddl_encode_sid(tmp_ctx, sd->group_sid, domain_sid);
+ if (sid == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "G:%s", sid);
+ if (sddl == NULL) goto failed;
+ }
+
+ if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl != NULL) {
+ char *acl = sddl_encode_acl(tmp_ctx, sd->dacl, sd->type, domain_sid);
+ if (acl == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "D:%s", acl);
+ if (sddl == NULL) goto failed;
+ }
+
+ if ((sd->type & SEC_DESC_SACL_PRESENT) && sd->sacl != NULL) {
+ char *acl = sddl_encode_acl(tmp_ctx, sd->sacl, sd->type>>1, domain_sid);
+ if (acl == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "S:%s", acl);
+ if (sddl == NULL) goto failed;
+ }
+
+ talloc_free(tmp_ctx);
+ return sddl;
+
+failed:
+ talloc_free(sddl);
+ return NULL;
+}
+
+
diff --git a/source4/libcli/security/security.h b/source4/libcli/security/security.h
new file mode 100644
index 0000000000..2608c9f7ed
--- /dev/null
+++ b/source4/libcli/security/security.h
@@ -0,0 +1,35 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2006
+
+ 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/security.h"
+
+enum security_user_level {
+ SECURITY_ANONYMOUS,
+ SECURITY_USER,
+ SECURITY_ADMINISTRATOR,
+ SECURITY_SYSTEM
+};
+
+struct auth_session_info;
+
+/* Moved the dom_sid functions to the top level dir with manual proto header */
+#include "libcli/security/dom_sid.h"
+#include "libcli/security/secace.h"
+#include "libcli/security/secacl.h"
+#include "libcli/security/proto.h"
diff --git a/source4/libcli/security/security_descriptor.c b/source4/libcli/security/security_descriptor.c
new file mode 100644
index 0000000000..8e9c7eb4a9
--- /dev/null
+++ b/source4/libcli/security/security_descriptor.c
@@ -0,0 +1,533 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security descriptror utility functions
+
+ 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/security/security.h"
+
+/*
+ return a blank security descriptor (no owners, dacl or sacl)
+*/
+struct security_descriptor *security_descriptor_initialise(TALLOC_CTX *mem_ctx)
+{
+ struct security_descriptor *sd;
+
+ sd = talloc(mem_ctx, struct security_descriptor);
+ if (!sd) {
+ return NULL;
+ }
+
+ sd->revision = SD_REVISION;
+ /* we mark as self relative, even though it isn't while it remains
+ a pointer in memory because this simplifies the ndr code later.
+ All SDs that we store/emit are in fact SELF_RELATIVE
+ */
+ sd->type = SEC_DESC_SELF_RELATIVE;
+
+ sd->owner_sid = NULL;
+ sd->group_sid = NULL;
+ sd->sacl = NULL;
+ sd->dacl = NULL;
+
+ return sd;
+}
+
+static struct security_acl *security_acl_dup(TALLOC_CTX *mem_ctx,
+ const struct security_acl *oacl)
+{
+ struct security_acl *nacl;
+
+ nacl = talloc (mem_ctx, struct security_acl);
+ if (nacl == NULL) {
+ return NULL;
+ }
+
+ nacl->aces = (struct security_ace *)talloc_memdup (nacl, oacl->aces, sizeof(struct security_ace) * oacl->num_aces);
+ if ((nacl->aces == NULL) && (oacl->num_aces > 0)) {
+ goto failed;
+ }
+
+ nacl->revision = oacl->revision;
+ nacl->size = oacl->size;
+ nacl->num_aces = oacl->num_aces;
+
+ return nacl;
+
+ failed:
+ talloc_free (nacl);
+ return NULL;
+
+}
+
+/*
+ talloc and copy a security descriptor
+ */
+struct security_descriptor *security_descriptor_copy(TALLOC_CTX *mem_ctx,
+ const struct security_descriptor *osd)
+{
+ struct security_descriptor *nsd;
+
+ nsd = talloc_zero(mem_ctx, struct security_descriptor);
+ if (!nsd) {
+ return NULL;
+ }
+
+ if (osd->owner_sid) {
+ nsd->owner_sid = dom_sid_dup(nsd, osd->owner_sid);
+ if (nsd->owner_sid == NULL) {
+ goto failed;
+ }
+ }
+
+ if (osd->group_sid) {
+ nsd->group_sid = dom_sid_dup(nsd, osd->group_sid);
+ if (nsd->group_sid == NULL) {
+ goto failed;
+ }
+ }
+
+ if (osd->sacl) {
+ nsd->sacl = security_acl_dup(nsd, osd->sacl);
+ if (nsd->sacl == NULL) {
+ goto failed;
+ }
+ }
+
+ if (osd->dacl) {
+ nsd->dacl = security_acl_dup(nsd, osd->dacl);
+ if (nsd->dacl == NULL) {
+ goto failed;
+ }
+ }
+
+ nsd->revision = osd->revision;
+ nsd->type = osd->type;
+
+ return nsd;
+
+ failed:
+ talloc_free(nsd);
+
+ return NULL;
+}
+
+/*
+ add an ACE to an ACL of a security_descriptor
+*/
+
+static NTSTATUS security_descriptor_acl_add(struct security_descriptor *sd,
+ bool add_to_sacl,
+ const struct security_ace *ace)
+{
+ struct security_acl *acl = NULL;
+
+ if (add_to_sacl) {
+ acl = sd->sacl;
+ } else {
+ acl = sd->dacl;
+ }
+
+ if (acl == NULL) {
+ acl = talloc(sd, struct security_acl);
+ if (acl == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ acl->revision = SECURITY_ACL_REVISION_NT4;
+ acl->size = 0;
+ acl->num_aces = 0;
+ acl->aces = NULL;
+ }
+
+ acl->aces = talloc_realloc(acl, acl->aces,
+ struct security_ace, acl->num_aces+1);
+ if (acl->aces == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ acl->aces[acl->num_aces] = *ace;
+
+ switch (acl->aces[acl->num_aces].type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
+ acl->revision = SECURITY_ACL_REVISION_ADS;
+ break;
+ default:
+ break;
+ }
+
+ acl->num_aces++;
+
+ if (add_to_sacl) {
+ sd->sacl = acl;
+ sd->type |= SEC_DESC_SACL_PRESENT;
+ } else {
+ sd->dacl = acl;
+ sd->type |= SEC_DESC_DACL_PRESENT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ add an ACE to the SACL of a security_descriptor
+*/
+
+NTSTATUS security_descriptor_sacl_add(struct security_descriptor *sd,
+ const struct security_ace *ace)
+{
+ return security_descriptor_acl_add(sd, true, ace);
+}
+
+/*
+ add an ACE to the DACL of a security_descriptor
+*/
+
+NTSTATUS security_descriptor_dacl_add(struct security_descriptor *sd,
+ const struct security_ace *ace)
+{
+ return security_descriptor_acl_add(sd, false, ace);
+}
+
+/*
+ delete the ACE corresponding to the given trustee in an ACL of a
+ security_descriptor
+*/
+
+static NTSTATUS security_descriptor_acl_del(struct security_descriptor *sd,
+ bool sacl_del,
+ const struct dom_sid *trustee)
+{
+ int i;
+ bool found = false;
+ struct security_acl *acl = NULL;
+
+ if (sacl_del) {
+ acl = sd->sacl;
+ } else {
+ acl = sd->dacl;
+ }
+
+ if (acl == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* there can be multiple ace's for one trustee */
+ for (i=0;i<acl->num_aces;i++) {
+ if (dom_sid_equal(trustee, &acl->aces[i].trustee)) {
+ memmove(&acl->aces[i], &acl->aces[i+1],
+ sizeof(acl->aces[i]) * (acl->num_aces - (i+1)));
+ acl->num_aces--;
+ if (acl->num_aces == 0) {
+ acl->aces = NULL;
+ }
+ found = true;
+ }
+ }
+
+ if (!found) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ acl->revision = SECURITY_ACL_REVISION_NT4;
+
+ for (i=0;i<acl->num_aces;i++) {
+ switch (acl->aces[i].type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
+ acl->revision = SECURITY_ACL_REVISION_ADS;
+ return NT_STATUS_OK;
+ default:
+ break; /* only for the switch statement */
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ delete the ACE corresponding to the given trustee in the DACL of a
+ security_descriptor
+*/
+
+NTSTATUS security_descriptor_dacl_del(struct security_descriptor *sd,
+ const struct dom_sid *trustee)
+{
+ return security_descriptor_acl_del(sd, false, trustee);
+}
+
+/*
+ delete the ACE corresponding to the given trustee in the SACL of a
+ security_descriptor
+*/
+
+NTSTATUS security_descriptor_sacl_del(struct security_descriptor *sd,
+ const struct dom_sid *trustee)
+{
+ return security_descriptor_acl_del(sd, true, trustee);
+}
+
+/*
+ compare two security ace structures
+*/
+bool security_ace_equal(const struct security_ace *ace1,
+ const struct security_ace *ace2)
+{
+ if (ace1 == ace2) return true;
+ if (!ace1 || !ace2) return false;
+ if (ace1->type != ace2->type) return false;
+ if (ace1->flags != ace2->flags) return false;
+ if (ace1->access_mask != ace2->access_mask) return false;
+ if (!dom_sid_equal(&ace1->trustee, &ace2->trustee)) return false;
+
+ return true;
+}
+
+
+/*
+ compare two security acl structures
+*/
+bool security_acl_equal(const struct security_acl *acl1,
+ const struct security_acl *acl2)
+{
+ int i;
+
+ if (acl1 == acl2) return true;
+ if (!acl1 || !acl2) return false;
+ if (acl1->revision != acl2->revision) return false;
+ if (acl1->num_aces != acl2->num_aces) return false;
+
+ for (i=0;i<acl1->num_aces;i++) {
+ if (!security_ace_equal(&acl1->aces[i], &acl2->aces[i])) return false;
+ }
+ return true;
+}
+
+/*
+ compare two security descriptors.
+*/
+bool security_descriptor_equal(const struct security_descriptor *sd1,
+ const struct security_descriptor *sd2)
+{
+ if (sd1 == sd2) return true;
+ if (!sd1 || !sd2) return false;
+ if (sd1->revision != sd2->revision) return false;
+ if (sd1->type != sd2->type) return false;
+
+ if (!dom_sid_equal(sd1->owner_sid, sd2->owner_sid)) return false;
+ if (!dom_sid_equal(sd1->group_sid, sd2->group_sid)) return false;
+ if (!security_acl_equal(sd1->sacl, sd2->sacl)) return false;
+ if (!security_acl_equal(sd1->dacl, sd2->dacl)) return false;
+
+ return true;
+}
+
+/*
+ compare two security descriptors, but allow certain (missing) parts
+ to be masked out of the comparison
+*/
+bool security_descriptor_mask_equal(const struct security_descriptor *sd1,
+ const struct security_descriptor *sd2,
+ uint32_t mask)
+{
+ if (sd1 == sd2) return true;
+ if (!sd1 || !sd2) return false;
+ if (sd1->revision != sd2->revision) return false;
+ if ((sd1->type & mask) != (sd2->type & mask)) return false;
+
+ if (!dom_sid_equal(sd1->owner_sid, sd2->owner_sid)) return false;
+ if (!dom_sid_equal(sd1->group_sid, sd2->group_sid)) return false;
+ if ((mask & SEC_DESC_DACL_PRESENT) && !security_acl_equal(sd1->dacl, sd2->dacl)) return false;
+ if ((mask & SEC_DESC_SACL_PRESENT) && !security_acl_equal(sd1->sacl, sd2->sacl)) return false;
+
+ return true;
+}
+
+
+static struct security_descriptor *security_descriptor_appendv(struct security_descriptor *sd,
+ bool add_ace_to_sacl,
+ va_list ap)
+{
+ const char *sidstr;
+
+ while ((sidstr = va_arg(ap, const char *))) {
+ struct dom_sid *sid;
+ struct security_ace *ace = talloc(sd, struct security_ace);
+ NTSTATUS status;
+
+ if (ace == NULL) {
+ talloc_free(sd);
+ return NULL;
+ }
+ ace->type = va_arg(ap, unsigned int);
+ ace->access_mask = va_arg(ap, unsigned int);
+ ace->flags = va_arg(ap, unsigned int);
+ sid = dom_sid_parse_talloc(ace, sidstr);
+ if (sid == NULL) {
+ talloc_free(sd);
+ return NULL;
+ }
+ ace->trustee = *sid;
+ if (add_ace_to_sacl) {
+ status = security_descriptor_sacl_add(sd, ace);
+ } else {
+ status = security_descriptor_dacl_add(sd, ace);
+ }
+ /* TODO: check: would talloc_free(ace) here be correct? */
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(sd);
+ return NULL;
+ }
+ }
+
+ return sd;
+}
+
+struct security_descriptor *security_descriptor_append(struct security_descriptor *sd,
+ ...)
+{
+ va_list ap;
+
+ va_start(ap, sd);
+ sd = security_descriptor_appendv(sd, false, ap);
+ va_end(ap);
+
+ return sd;
+}
+
+static struct security_descriptor *security_descriptor_createv(TALLOC_CTX *mem_ctx,
+ uint16_t sd_type,
+ const char *owner_sid,
+ const char *group_sid,
+ bool add_ace_to_sacl,
+ va_list ap)
+{
+ struct security_descriptor *sd;
+
+ sd = security_descriptor_initialise(mem_ctx);
+ if (sd == NULL) {
+ return NULL;
+ }
+
+ sd->type |= sd_type;
+
+ if (owner_sid) {
+ sd->owner_sid = dom_sid_parse_talloc(sd, owner_sid);
+ if (sd->owner_sid == NULL) {
+ talloc_free(sd);
+ return NULL;
+ }
+ }
+ if (group_sid) {
+ sd->group_sid = dom_sid_parse_talloc(sd, group_sid);
+ if (sd->group_sid == NULL) {
+ talloc_free(sd);
+ return NULL;
+ }
+ }
+
+ return security_descriptor_appendv(sd, add_ace_to_sacl, ap);
+}
+
+/*
+ create a security descriptor using string SIDs. This is used by the
+ torture code to allow the easy creation of complex ACLs
+ This is a varargs function. The list of DACL ACEs ends with a NULL sid.
+
+ Each ACE contains a set of 4 parameters:
+ SID, ACCESS_TYPE, MASK, FLAGS
+
+ a typical call would be:
+
+ sd = security_descriptor_dacl_create(mem_ctx,
+ sd_type_flags,
+ mysid,
+ mygroup,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+ that would create a sd with one DACL ACE
+*/
+
+struct security_descriptor *security_descriptor_dacl_create(TALLOC_CTX *mem_ctx,
+ uint16_t sd_type,
+ const char *owner_sid,
+ const char *group_sid,
+ ...)
+{
+ struct security_descriptor *sd = NULL;
+ va_list ap;
+ va_start(ap, group_sid);
+ sd = security_descriptor_createv(mem_ctx, sd_type, owner_sid,
+ group_sid, false, ap);
+ va_end(ap);
+
+ return sd;
+}
+
+struct security_descriptor *security_descriptor_sacl_create(TALLOC_CTX *mem_ctx,
+ uint16_t sd_type,
+ const char *owner_sid,
+ const char *group_sid,
+ ...)
+{
+ struct security_descriptor *sd = NULL;
+ va_list ap;
+ va_start(ap, group_sid);
+ sd = security_descriptor_createv(mem_ctx, sd_type, owner_sid,
+ group_sid, true, ap);
+ va_end(ap);
+
+ return sd;
+}
+
+struct security_ace *security_ace_create(TALLOC_CTX *mem_ctx,
+ const char *sid_str,
+ enum security_ace_type type,
+ uint32_t access_mask,
+ uint8_t flags)
+
+{
+ struct dom_sid *sid;
+ struct security_ace *ace;
+
+ ace = talloc_zero(mem_ctx, struct security_ace);
+ if (ace == NULL) {
+ return NULL;
+ }
+
+ sid = dom_sid_parse_talloc(ace, sid_str);
+ if (sid == NULL) {
+ talloc_free(ace);
+ return NULL;
+ }
+
+ ace->trustee = *sid;
+ ace->type = type;
+ ace->access_mask = access_mask;
+ ace->flags = flags;
+
+ return ace;
+}
diff --git a/source4/libcli/security/security_token.c b/source4/libcli/security/security_token.c
new file mode 100644
index 0000000000..e1349e06f8
--- /dev/null
+++ b/source4/libcli/security/security_token.c
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security descriptror utility functions
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Stefan Metzmacher 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 "includes.h"
+#include "libcli/security/security.h"
+#include "auth/session.h"
+
+/*
+ return a blank security token
+*/
+struct security_token *security_token_initialise(TALLOC_CTX *mem_ctx)
+{
+ struct security_token *st;
+
+ st = talloc(mem_ctx, struct security_token);
+ if (!st) {
+ return NULL;
+ }
+
+ st->user_sid = NULL;
+ st->group_sid = NULL;
+ st->num_sids = 0;
+ st->sids = NULL;
+ st->privilege_mask = 0;
+
+ return st;
+}
+
+/****************************************************************************
+ prints a struct security_token to debug output.
+****************************************************************************/
+void security_token_debug(int dbg_lev, const struct security_token *token)
+{
+ TALLOC_CTX *mem_ctx;
+ int i;
+
+ if (!token) {
+ DEBUG(dbg_lev, ("Security token: (NULL)\n"));
+ return;
+ }
+
+ mem_ctx = talloc_init("security_token_debug()");
+ if (!mem_ctx) {
+ return;
+ }
+
+ DEBUG(dbg_lev, ("Security token of user %s\n",
+ dom_sid_string(mem_ctx, token->user_sid) ));
+ DEBUGADD(dbg_lev, (" SIDs (%lu):\n",
+ (unsigned long)token->num_sids));
+ for (i = 0; i < token->num_sids; i++) {
+ DEBUGADD(dbg_lev, (" SID[%3lu]: %s\n", (unsigned long)i,
+ dom_sid_string(mem_ctx, token->sids[i])));
+ }
+
+ security_token_debug_privileges(dbg_lev, token);
+
+ talloc_free(mem_ctx);
+}
+
+/* These really should be cheaper... */
+
+bool security_token_is_sid(const struct security_token *token, const struct dom_sid *sid)
+{
+ if (dom_sid_equal(token->user_sid, sid)) {
+ return true;
+ }
+ return false;
+}
+
+bool security_token_is_sid_string(const struct security_token *token, const char *sid_string)
+{
+ bool ret;
+ struct dom_sid *sid = dom_sid_parse_talloc(NULL, sid_string);
+ if (!sid) return false;
+
+ ret = security_token_is_sid(token, sid);
+
+ talloc_free(sid);
+ return ret;
+}
+
+bool security_token_is_system(const struct security_token *token)
+{
+ return security_token_is_sid_string(token, SID_NT_SYSTEM);
+}
+
+bool security_token_is_anonymous(const struct security_token *token)
+{
+ return security_token_is_sid_string(token, SID_NT_ANONYMOUS);
+}
+
+bool security_token_has_sid(const struct security_token *token, const struct dom_sid *sid)
+{
+ int i;
+ for (i = 0; i < token->num_sids; i++) {
+ if (dom_sid_equal(token->sids[i], sid)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool security_token_has_sid_string(const struct security_token *token, const char *sid_string)
+{
+ bool ret;
+ struct dom_sid *sid = dom_sid_parse_talloc(NULL, sid_string);
+ if (!sid) return false;
+
+ ret = security_token_has_sid(token, sid);
+
+ talloc_free(sid);
+ return ret;
+}
+
+bool security_token_has_builtin_administrators(const struct security_token *token)
+{
+ return security_token_has_sid_string(token, SID_BUILTIN_ADMINISTRATORS);
+}
+
+bool security_token_has_nt_authenticated_users(const struct security_token *token)
+{
+ return security_token_has_sid_string(token, SID_NT_AUTHENTICATED_USERS);
+}
+
+enum security_user_level security_session_user_level(struct auth_session_info *session_info)
+{
+ if (!session_info) {
+ return SECURITY_ANONYMOUS;
+ }
+
+ if (security_token_is_system(session_info->security_token)) {
+ return SECURITY_SYSTEM;
+ }
+
+ if (security_token_is_anonymous(session_info->security_token)) {
+ return SECURITY_ANONYMOUS;
+ }
+
+ if (security_token_has_builtin_administrators(session_info->security_token)) {
+ return SECURITY_ADMINISTRATOR;
+ }
+
+ if (security_token_has_nt_authenticated_users(session_info->security_token)) {
+ return SECURITY_USER;
+ }
+
+ return SECURITY_ANONYMOUS;
+}
+
diff --git a/source4/libcli/security/tests/bindings.py b/source4/libcli/security/tests/bindings.py
new file mode 100644
index 0000000000..24ee01c37f
--- /dev/null
+++ b/source4/libcli/security/tests/bindings.py
@@ -0,0 +1,97 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+#
+# 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/>.
+#
+
+import unittest
+from samba.dcerpc import security
+
+class SecurityTokenTests(unittest.TestCase):
+ def setUp(self):
+ self.token = security.token()
+
+ def test_is_system(self):
+ self.assertFalse(self.token.is_system())
+
+ def test_is_anonymous(self):
+ self.assertFalse(self.token.is_anonymous())
+
+ def test_has_builtin_administrators(self):
+ self.assertFalse(self.token.has_builtin_administrators())
+
+ def test_has_nt_authenticated_users(self):
+ self.assertFalse(self.token.has_nt_authenticated_users())
+
+ def test_has_priv(self):
+ self.assertFalse(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN))
+
+ def test_set_priv(self):
+ self.assertFalse(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN))
+ self.assertFalse(self.token.set_privilege(security.SEC_PRIV_SHUTDOWN))
+ self.assertTrue(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN))
+
+
+class SecurityDescriptorTests(unittest.TestCase):
+ def setUp(self):
+ self.descriptor = security.descriptor()
+
+ def test_from_sddl(self):
+ desc = security.descriptor.from_sddl("O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)", security.dom_sid("S-2-0-0"))
+ self.assertEquals(desc.group_sid, security.dom_sid('S-2-0-0-512'))
+ self.assertEquals(desc.owner_sid, security.dom_sid('S-1-5-32-548'))
+ self.assertEquals(desc.revision, 1)
+ self.assertEquals(desc.sacl, None)
+ self.assertEquals(desc.type, 0x8004)
+
+ def test_as_sddl(self):
+ text = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)"
+ dom = security.dom_sid("S-2-0-0")
+ desc1 = security.descriptor.from_sddl(text, dom)
+ desc2 = security.descriptor.from_sddl(desc1.as_sddl(dom), dom)
+ self.assertEquals(desc1.group_sid, desc2.group_sid)
+ self.assertEquals(desc1.owner_sid, desc2.owner_sid)
+ self.assertEquals(desc1.sacl, desc2.sacl)
+ self.assertEquals(desc1.type, desc2.type)
+
+
+class DomSidTests(unittest.TestCase):
+ def test_parse_sid(self):
+ sid = security.dom_sid("S-1-5-21")
+ self.assertEquals("S-1-5-21", str(sid))
+
+ def test_sid_equal(self):
+ sid1 = security.dom_sid("S-1-5-21")
+ sid2 = security.dom_sid("S-1-5-21")
+ self.assertEquals(sid1, sid1)
+ self.assertEquals(sid1, sid2)
+
+ def test_random(self):
+ sid = security.random_sid()
+ self.assertTrue(str(sid).startswith("S-1-5-21-"))
+
+ def test_repr(self):
+ sid = security.random_sid()
+ self.assertTrue(repr(sid).startswith("dom_sid('S-1-5-21-"))
+
+
+class PrivilegeTests(unittest.TestCase):
+ def test_privilege_name(self):
+ self.assertEquals("SeShutdownPrivilege", security.privilege_name(security.SEC_PRIV_SHUTDOWN))
+
+ def test_privilege_id(self):
+ self.assertEquals(security.SEC_PRIV_SHUTDOWN, security.privilege_id("SeShutdownPrivilege"))
+
diff --git a/source4/libcli/security/tests/sddl.c b/source4/libcli/security/tests/sddl.c
new file mode 100644
index 0000000000..9e7705ea92
--- /dev/null
+++ b/source4/libcli/security/tests/sddl.c
@@ -0,0 +1,105 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of SDDL parsing
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/security/security.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+
+/*
+ test one SDDL example
+*/
+static bool test_sddl(struct torture_context *tctx,
+ const void *test_data)
+{
+ struct security_descriptor *sd, *sd2;
+ struct dom_sid *domain;
+ const char *sddl = (const char *)test_data;
+ const char *sddl2;
+ TALLOC_CTX *mem_ctx = tctx;
+
+
+ domain = dom_sid_parse_talloc(mem_ctx, "S-1-2-3-4");
+ sd = sddl_decode(mem_ctx, sddl, domain);
+ torture_assert(tctx, sd != NULL, talloc_asprintf(tctx,
+ "Failed to decode '%s'\n", sddl));
+
+ sddl2 = sddl_encode(mem_ctx, sd, domain);
+ torture_assert(tctx, sddl2 != NULL, talloc_asprintf(tctx,
+ "Failed to re-encode '%s'\n", sddl));
+
+ sd2 = sddl_decode(mem_ctx, sddl2, domain);
+ torture_assert(tctx, sd2 != NULL, talloc_asprintf(tctx,
+ "Failed to decode2 '%s'\n", sddl2));
+
+ torture_assert(tctx, security_descriptor_equal(sd, sd2),
+ talloc_asprintf(tctx, "Failed equality test for '%s'\n", sddl));
+
+#if 0
+ /* flags don't have a canonical order ... */
+ if (strcmp(sddl, sddl2) != 0) {
+ printf("Failed sddl equality test\norig: %s\n new: %s\n", sddl, sddl2);
+ }
+#endif
+
+ if (DEBUGLVL(2)) {
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ }
+ talloc_free(sd);
+ talloc_free(domain);
+ return true;
+}
+
+static const char *examples[] = {
+ "D:(A;;CC;;;BA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",
+ "D:(A;;GA;;;SY)",
+ "D:(A;;RP;;;WD)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCRCWDWOSW;;;DA)(A;CI;RPWPCRLCLOCCRCWDWOSDSW;;;BA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EA)(A;CI;LC;;;RU)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)(A;;RPRC;;;RU)(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(A;;LCRPLORC;;;ED)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RPLCLORC;;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)S:(AU;SA;WDWOWP;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)",
+ "D:(A;;RPLCLORC;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPCRLCLORCSDDT;;;CO)(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)(A;;RPLCLORC;;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(A;;CCDC;;;PS)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a55-1e2f-11d0-9819-00aa0040529b;;AU)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)S:(AU;SA;CRWP;;;WD)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B2-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B3-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RP;037088f8-0ae1-11d2-b422-00a0c968f939;;RS)(OA;;RP;4c164200-20c0-11d0-a768-00aa006e0529;;RS)(OA;;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;;RS)(A;;RC;;;AU)(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)(OA;;RP;77B5B886-944A-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;E45795B3-9455-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(OA;;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;;RS)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)(OA;;WPRP;6db69a1c-9422-11d1-aebd-0000f80367c1;;S-1-5-32-561)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;LCRPLORC;;;ED)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(OA;;CCDC;bf967a86-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aba-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967a9c-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(A;;RPLCLORC;;;AU)(A;;LCRPLORC;;;ED)(OA;;CCDC;4828CC14-1437-45bc-9B07-AD6F015E5F28;;AO)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",
+ "D:(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",
+ "D:S:",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)"
+};
+
+/* test a set of example SDDL strings */
+struct torture_suite *torture_local_sddl(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "SDDL");
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(examples); i++) {
+ torture_suite_add_simple_tcase_const(suite,
+ talloc_asprintf(suite, "%d", i),
+ test_sddl, examples[i]);
+ }
+
+ return suite;
+}
diff --git a/source4/libcli/smb2/break.c b/source4/libcli/smb2/break.c
new file mode 100644
index 0000000000..fe0cceb829
--- /dev/null
+++ b/source4/libcli/smb2/break.c
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client oplock break handling
+
+ Copyright (C) Stefan Metzmacher 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/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a break request
+*/
+struct smb2_request *smb2_break_send(struct smb2_tree *tree, struct smb2_break *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_BREAK, 0x18, false, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.oplock_level);
+ SCVAL(req->out.body, 0x03, io->in.reserved);
+ SIVAL(req->out.body, 0x04, io->in.reserved2);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a break reply
+*/
+NTSTATUS smb2_break_recv(struct smb2_request *req, struct smb2_break *io)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x18, false);
+
+ io->out.oplock_level = CVAL(req->in.body, 0x02);
+ io->out.reserved = CVAL(req->in.body, 0x03);
+ io->out.reserved2 = IVAL(req->in.body, 0x04);
+ smb2_pull_handle(req->in.body+0x08, &io->out.file.handle);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync flush request
+*/
+NTSTATUS smb2_break(struct smb2_tree *tree, struct smb2_break *io)
+{
+ struct smb2_request *req = smb2_break_send(tree, io);
+ return smb2_break_recv(req, io);
+}
diff --git a/source4/libcli/smb2/cancel.c b/source4/libcli/smb2/cancel.c
new file mode 100644
index 0000000000..65f02187c1
--- /dev/null
+++ b/source4/libcli/smb2/cancel.c
@@ -0,0 +1,77 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client notify calls
+
+ Copyright (C) Stefan Metzmacher 2006
+
+ 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/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a cancel request
+*/
+NTSTATUS smb2_cancel(struct smb2_request *r)
+{
+ NTSTATUS status;
+ struct smb2_request *c;
+ uint32_t old_timeout;
+ uint64_t old_seqnum;
+
+ /*
+ * if we don't get a pending id yet, we just
+ * mark the request for pending, so that we directly
+ * send the cancel after getting the pending id
+ */
+ if (!r->cancel.can_cancel) {
+ r->cancel.do_cancel++;
+ return NT_STATUS_OK;
+ }
+
+ /* we don't want a seqmun for a SMB2 Cancel */
+ old_seqnum = r->transport->seqnum;
+ c = smb2_request_init(r->transport, SMB2_OP_CANCEL, 0x04, false, 0);
+ r->transport->seqnum = old_seqnum;
+ NT_STATUS_HAVE_NO_MEMORY(c);
+ c->seqnum = 0;
+
+ SIVAL(c->out.hdr, SMB2_HDR_FLAGS, 0x00000002);
+ SSVAL(c->out.hdr, SMB2_HDR_CREDIT, 0x0030);
+ SIVAL(c->out.hdr, SMB2_HDR_PID, r->cancel.pending_id);
+ SBVAL(c->out.hdr, SMB2_HDR_MESSAGE_ID, c->seqnum);
+ if (r->session) {
+ SBVAL(c->out.hdr, SMB2_HDR_SESSION_ID, r->session->uid);
+ }
+
+ SSVAL(c->out.body, 0x02, 0);
+
+ old_timeout = c->transport->options.request_timeout;
+ c->transport->options.request_timeout = 0;
+ smb2_transport_send(c);
+ c->transport->options.request_timeout = old_timeout;
+
+ if (c->state == SMB2_REQUEST_ERROR) {
+ status = c->status;
+ } else {
+ status = NT_STATUS_OK;
+ }
+
+ talloc_free(c);
+ return status;
+}
diff --git a/source4/libcli/smb2/close.c b/source4/libcli/smb2/close.c
new file mode 100644
index 0000000000..4e6f33095f
--- /dev/null
+++ b/source4/libcli/smb2/close.c
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client close handling
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a close request
+*/
+struct smb2_request *smb2_close_send(struct smb2_tree *tree, struct smb2_close *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_CLOSE, 0x18, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, io->in.flags);
+ SIVAL(req->out.body, 0x04, 0); /* pad */
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a close reply
+*/
+NTSTATUS smb2_close_recv(struct smb2_request *req, struct smb2_close *io)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x3C, false);
+
+ io->out.flags = SVAL(req->in.body, 0x02);
+ io->out._pad = IVAL(req->in.body, 0x04);
+ io->out.create_time = smbcli_pull_nttime(req->in.body, 0x08);
+ io->out.access_time = smbcli_pull_nttime(req->in.body, 0x10);
+ io->out.write_time = smbcli_pull_nttime(req->in.body, 0x18);
+ io->out.change_time = smbcli_pull_nttime(req->in.body, 0x20);
+ io->out.alloc_size = BVAL(req->in.body, 0x28);
+ io->out.size = BVAL(req->in.body, 0x30);
+ io->out.file_attr = IVAL(req->in.body, 0x38);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync close request
+*/
+NTSTATUS smb2_close(struct smb2_tree *tree, struct smb2_close *io)
+{
+ struct smb2_request *req = smb2_close_send(tree, io);
+ return smb2_close_recv(req, io);
+}
diff --git a/source4/libcli/smb2/config.mk b/source4/libcli/smb2/config.mk
new file mode 100644
index 0000000000..322bca1416
--- /dev/null
+++ b/source4/libcli/smb2/config.mk
@@ -0,0 +1,10 @@
+[SUBSYSTEM::LIBCLI_SMB2]
+PUBLIC_DEPENDENCIES = LIBCLI_RAW LIBPACKET gensec
+
+LIBCLI_SMB2_OBJ_FILES = $(addprefix $(libclisrcdir)/smb2/, \
+ transport.o request.o negprot.o session.o tcon.o \
+ create.o close.o connect.o getinfo.o write.o read.o \
+ setinfo.o find.o ioctl.o logoff.o tdis.o flush.o \
+ lock.o notify.o cancel.o keepalive.o break.o util.o signing.o)
+
+$(eval $(call proto_header_template,$(libclisrcdir)/smb2/smb2_proto.h,$(LIBCLI_SMB2_OBJ_FILES:.o=.c)))
diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c
new file mode 100644
index 0000000000..11bec42737
--- /dev/null
+++ b/source4/libcli/smb2/connect.c
@@ -0,0 +1,311 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 composite connection setup
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/composite/composite.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+struct smb2_connect_state {
+ struct cli_credentials *credentials;
+ struct resolve_context *resolve_ctx;
+ const char *host;
+ const char *share;
+ const char **ports;
+ const char *socket_options;
+ struct gensec_settings *gensec_settings;
+ struct smbcli_options options;
+ struct smb2_negprot negprot;
+ struct smb2_tree_connect tcon;
+ struct smb2_session *session;
+ struct smb2_tree *tree;
+};
+
+/*
+ continue after tcon reply
+*/
+static void continue_tcon(struct smb2_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+
+ c->status = smb2_tree_connect_recv(req, &state->tcon);
+ if (!composite_is_ok(c)) return;
+
+ state->tree->tid = state->tcon.out.tid;
+
+ composite_done(c);
+}
+
+/*
+ continue after a session setup
+*/
+static void continue_session(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ struct smb2_request *req;
+
+ c->status = smb2_session_setup_spnego_recv(creq);
+ if (!composite_is_ok(c)) return;
+
+ state->tree = smb2_tree_init(state->session, state, true);
+ if (composite_nomem(state->tree, c)) return;
+
+ state->tcon.in.reserved = 0;
+ state->tcon.in.path = talloc_asprintf(state, "\\\\%s\\%s",
+ state->host, state->share);
+ if (composite_nomem(state->tcon.in.path, c)) return;
+
+ req = smb2_tree_connect_send(state->tree, &state->tcon);
+ if (composite_nomem(req, c)) return;
+
+ req->async.fn = continue_tcon;
+ req->async.private_data = c;
+}
+
+/*
+ continue after negprot reply
+*/
+static void continue_negprot(struct smb2_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ struct smb2_transport *transport = req->transport;
+ struct composite_context *creq;
+
+ c->status = smb2_negprot_recv(req, c, &state->negprot);
+ if (!composite_is_ok(c)) return;
+
+ transport->negotiate.system_time = state->negprot.out.system_time;
+ transport->negotiate.server_start_time = state->negprot.out.server_start_time;
+ transport->negotiate.security_mode = state->negprot.out.security_mode;
+
+ switch (transport->options.signing) {
+ case SMB_SIGNING_OFF:
+ if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+ composite_error(c, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ transport->signing_required = false;
+ break;
+ case SMB_SIGNING_SUPPORTED:
+ if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+ transport->signing_required = true;
+ } else {
+ transport->signing_required = false;
+ }
+ break;
+ case SMB_SIGNING_AUTO:
+ if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) {
+ transport->signing_required = true;
+ } else {
+ transport->signing_required = false;
+ }
+ break;
+ case SMB_SIGNING_REQUIRED:
+ if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) {
+ transport->signing_required = true;
+ } else {
+ composite_error(c, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ break;
+ }
+
+ state->session = smb2_session_init(transport, state->gensec_settings, state, true);
+ if (composite_nomem(state->session, c)) return;
+
+ creq = smb2_session_setup_spnego_send(state->session, state->credentials);
+
+ composite_continue(c, creq, continue_session, c);
+}
+
+/*
+ continue after a socket connect completes
+*/
+static void continue_socket(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ struct smbcli_socket *sock;
+ struct smb2_transport *transport;
+ struct smb2_request *req;
+ uint16_t dialects[2];
+
+ c->status = smbcli_sock_connect_recv(creq, state, &sock);
+ if (!composite_is_ok(c)) return;
+
+ transport = smb2_transport_init(sock, state, &state->options);
+ if (composite_nomem(transport, c)) return;
+
+ ZERO_STRUCT(state->negprot);
+ state->negprot.in.dialect_count = 2;
+ switch (transport->options.signing) {
+ case SMB_SIGNING_OFF:
+ state->negprot.in.security_mode = 0;
+ break;
+ case SMB_SIGNING_SUPPORTED:
+ case SMB_SIGNING_AUTO:
+ state->negprot.in.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+ break;
+ case SMB_SIGNING_REQUIRED:
+ state->negprot.in.security_mode =
+ SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ break;
+ }
+ state->negprot.in.capabilities = 0;
+ unix_to_nt_time(&state->negprot.in.start_time, time(NULL));
+ dialects[0] = SMB2_DIALECT_REVISION;
+ dialects[1] = 0;
+ state->negprot.in.dialects = dialects;
+
+ req = smb2_negprot_send(transport, &state->negprot);
+ if (composite_nomem(req, c)) return;
+
+ req->async.fn = continue_negprot;
+ req->async.private_data = c;
+}
+
+
+/*
+ continue after a resolve finishes
+*/
+static void continue_resolve(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ const char *addr;
+ const char **ports;
+ const char *default_ports[] = { "445", NULL };
+
+ c->status = resolve_name_recv(creq, state, &addr);
+ if (!composite_is_ok(c)) return;
+
+ if (state->ports == NULL) {
+ ports = default_ports;
+ } else {
+ ports = state->ports;
+ }
+
+ creq = smbcli_sock_connect_send(state, addr, ports, state->host, state->resolve_ctx, c->event_ctx, state->socket_options);
+
+ composite_continue(c, creq, continue_socket, c);
+}
+
+/*
+ a composite function that does a full negprot/sesssetup/tcon, returning
+ a connected smb2_tree
+ */
+struct composite_context *smb2_connect_send(TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char **ports,
+ const char *share,
+ struct resolve_context *resolve_ctx,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct smbcli_options *options,
+ const char *socket_options,
+ struct gensec_settings *gensec_settings)
+{
+ struct composite_context *c;
+ struct smb2_connect_state *state;
+ struct nbt_name name;
+ struct composite_context *creq;
+
+ c = composite_create(mem_ctx, ev);
+ if (c == NULL) return NULL;
+
+ state = talloc(c, struct smb2_connect_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->credentials = credentials;
+ state->options = *options;
+ state->host = talloc_strdup(c, host);
+ if (composite_nomem(state->host, c)) return c;
+ state->ports = talloc_reference(state, ports);
+ state->share = talloc_strdup(c, share);
+ if (composite_nomem(state->share, c)) return c;
+ state->resolve_ctx = talloc_reference(state, resolve_ctx);
+ state->socket_options = talloc_reference(state, socket_options);
+ state->gensec_settings = talloc_reference(state, gensec_settings);
+
+ ZERO_STRUCT(name);
+ name.name = host;
+
+ creq = resolve_name_send(resolve_ctx, &name, c->event_ctx);
+ composite_continue(c, creq, continue_resolve, c);
+ return c;
+}
+
+/*
+ receive a connect reply
+*/
+NTSTATUS smb2_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct smb2_tree **tree)
+{
+ NTSTATUS status;
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ *tree = talloc_steal(mem_ctx, state->tree);
+ }
+ talloc_free(c);
+ return status;
+}
+
+/*
+ sync version of smb2_connect
+*/
+NTSTATUS smb2_connect(TALLOC_CTX *mem_ctx,
+ const char *host, const char **ports,
+ const char *share,
+ struct resolve_context *resolve_ctx,
+ struct cli_credentials *credentials,
+ struct smb2_tree **tree,
+ struct tevent_context *ev,
+ struct smbcli_options *options,
+ const char *socket_options,
+ struct gensec_settings *gensec_settings)
+{
+ struct composite_context *c = smb2_connect_send(mem_ctx, host, ports,
+ share, resolve_ctx,
+ credentials, ev, options,
+ socket_options,
+ gensec_settings);
+ return smb2_connect_recv(c, mem_ctx, tree);
+}
diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c
new file mode 100644
index 0000000000..8a40e56a00
--- /dev/null
+++ b/source4/libcli/smb2/create.c
@@ -0,0 +1,419 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client tree handling
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+
+/*
+ parse a set of SMB2 create blobs
+*/
+NTSTATUS smb2_create_blob_parse(TALLOC_CTX *mem_ctx, const DATA_BLOB buffer,
+ struct smb2_create_blobs *blobs)
+{
+ const uint8_t *data = buffer.data;
+ uint32_t remaining = buffer.length;
+
+ while (remaining > 0) {
+ uint32_t next;
+ uint32_t name_offset, name_length;
+ uint32_t reserved, data_offset;
+ uint32_t data_length;
+ char *tag;
+ DATA_BLOB b;
+ NTSTATUS status;
+
+ if (remaining < 16) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ next = IVAL(data, 0);
+ name_offset = SVAL(data, 4);
+ name_length = SVAL(data, 6);
+ reserved = SVAL(data, 8);
+ data_offset = SVAL(data, 10);
+ data_length = IVAL(data, 12);
+
+ if ((next & 0x7) != 0 ||
+ next > remaining ||
+ name_offset < 16 ||
+ name_offset > remaining ||
+ name_length != 4 || /* windows enforces this */
+ name_offset + name_length > remaining ||
+ data_offset < name_offset + name_length ||
+ data_offset > remaining ||
+ data_offset + (uint64_t)data_length > remaining) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ tag = talloc_strndup(mem_ctx, (const char *)data + name_offset, name_length);
+ if (tag == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ b = data_blob_const(data+data_offset, data_length);
+ status = smb2_create_blob_add(mem_ctx, blobs, tag, b);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ talloc_free(tag);
+
+ if (next == 0) break;
+
+ remaining -= next;
+ data += next;
+
+ if (remaining < 16) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ add a blob to a smb2_create attribute blob
+*/
+static NTSTATUS smb2_create_blob_push_one(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer,
+ const struct smb2_create_blob *blob,
+ bool last)
+{
+ uint32_t ofs = buffer->length;
+ size_t tag_length = strlen(blob->tag);
+ uint8_t pad = smb2_padding_size(blob->data.length+tag_length, 4);
+
+ if (!data_blob_realloc(mem_ctx, buffer,
+ buffer->length + 0x14 + tag_length + blob->data.length + pad))
+ return NT_STATUS_NO_MEMORY;
+
+ if (last) {
+ SIVAL(buffer->data, ofs+0x00, 0);
+ } else {
+ SIVAL(buffer->data, ofs+0x00, 0x14 + tag_length + blob->data.length + pad);
+ }
+ SSVAL(buffer->data, ofs+0x04, 0x10); /* offset of tag */
+ SIVAL(buffer->data, ofs+0x06, tag_length); /* tag length */
+ SSVAL(buffer->data, ofs+0x0A, 0x14 + tag_length); /* offset of data */
+ SIVAL(buffer->data, ofs+0x0C, blob->data.length);
+ memcpy(buffer->data+ofs+0x10, blob->tag, tag_length);
+ SIVAL(buffer->data, ofs+0x10+tag_length, 0); /* pad? */
+ memcpy(buffer->data+ofs+0x14+tag_length, blob->data.data, blob->data.length);
+ memset(buffer->data+ofs+0x14+tag_length+blob->data.length, 0, pad);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ create a buffer of a set of create blobs
+*/
+NTSTATUS smb2_create_blob_push(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer,
+ const struct smb2_create_blobs blobs)
+{
+ int i;
+ NTSTATUS status;
+
+ *buffer = data_blob(NULL, 0);
+ for (i=0; i < blobs.num_blobs; i++) {
+ bool last = false;
+ const struct smb2_create_blob *c;
+
+ if ((i + 1) == blobs.num_blobs) {
+ last = true;
+ }
+
+ c = &blobs.blobs[i];
+ status = smb2_create_blob_push_one(mem_ctx, buffer, c, last);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS smb2_create_blob_add(TALLOC_CTX *mem_ctx, struct smb2_create_blobs *b,
+ const char *tag, DATA_BLOB data)
+{
+ struct smb2_create_blob *array;
+
+ array = talloc_realloc(mem_ctx, b->blobs,
+ struct smb2_create_blob,
+ b->num_blobs + 1);
+ NT_STATUS_HAVE_NO_MEMORY(array);
+ b->blobs = array;
+
+ b->blobs[b->num_blobs].tag = talloc_strdup(b->blobs, tag);
+ NT_STATUS_HAVE_NO_MEMORY(b->blobs[b->num_blobs].tag);
+
+ if (data.data) {
+ b->blobs[b->num_blobs].data = data_blob_talloc(b->blobs,
+ data.data,
+ data.length);
+ NT_STATUS_HAVE_NO_MEMORY(b->blobs[b->num_blobs].data.data);
+ } else {
+ b->blobs[b->num_blobs].data = data_blob(NULL, 0);
+ }
+
+ b->num_blobs += 1;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ send a create request
+*/
+struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+ DATA_BLOB blob;
+ struct smb2_create_blobs blobs;
+ int i;
+
+ ZERO_STRUCT(blobs);
+
+ req = smb2_request_init_tree(tree, SMB2_OP_CREATE, 0x38, true, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.security_flags);
+ SCVAL(req->out.body, 0x03, io->in.oplock_level);
+ SIVAL(req->out.body, 0x04, io->in.impersonation_level);
+ SBVAL(req->out.body, 0x08, io->in.create_flags);
+ SBVAL(req->out.body, 0x10, io->in.reserved);
+ SIVAL(req->out.body, 0x18, io->in.desired_access);
+ SIVAL(req->out.body, 0x1C, io->in.file_attributes);
+ SIVAL(req->out.body, 0x20, io->in.share_access);
+ SIVAL(req->out.body, 0x24, io->in.create_disposition);
+ SIVAL(req->out.body, 0x28, io->in.create_options);
+
+ status = smb2_push_o16s16_string(&req->out, 0x2C, io->in.fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ /* now add all the optional blobs */
+ if (io->in.eas.num_eas != 0) {
+ DATA_BLOB b = data_blob_talloc(req, NULL,
+ ea_list_size_chained(io->in.eas.num_eas, io->in.eas.eas, 4));
+ ea_put_list_chained(b.data, io->in.eas.num_eas, io->in.eas.eas, 4);
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_EXTA, b);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ data_blob_free(&b);
+ }
+
+ /* an empty MxAc tag seems to be used to ask the server to
+ return the maximum access mask allowed on the file */
+ if (io->in.query_maximal_access) {
+ /* TODO: MS-SMB2 2.2.13.2.5 says this can contain a timestamp? What to do
+ with that if it doesn't match? */
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_MXAC, data_blob(NULL, 0));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.alloc_size != 0) {
+ uint8_t data[8];
+ SBVAL(data, 0, io->in.alloc_size);
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_ALSI, data_blob_const(data, 8));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.durable_open) {
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_DHNQ, data_blob_talloc_zero(req, 16));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.durable_handle) {
+ uint8_t data[16];
+ smb2_push_handle(data, io->in.durable_handle);
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_DHNC, data_blob_const(data, 16));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.timewarp) {
+ uint8_t data[8];
+ SBVAL(data, 0, io->in.timewarp);
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_TWRP, data_blob_const(data, 8));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.sec_desc) {
+ enum ndr_err_code ndr_err;
+ DATA_BLOB sd_blob;
+ ndr_err = ndr_push_struct_blob(&sd_blob, req, NULL,
+ io->in.sec_desc,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(req);
+ return NULL;
+ }
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_SECD, sd_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.query_on_disk_id) {
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_QFID, data_blob(NULL, 0));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ /* and any custom blobs */
+ for (i=0;i<io->in.blobs.num_blobs;i++) {
+ status = smb2_create_blob_add(req, &blobs,
+ io->in.blobs.blobs[i].tag,
+ io->in.blobs.blobs[i].data);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+
+ status = smb2_create_blob_push(req, &blob, blobs);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ status = smb2_push_o32s32_blob(&req->out, 0x30, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ data_blob_free(&blob);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a create reply
+*/
+NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct smb2_create *io)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ int i;
+
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x58, true);
+ ZERO_STRUCT(io->out);
+ io->out.oplock_level = CVAL(req->in.body, 0x02);
+ io->out.reserved = CVAL(req->in.body, 0x03);
+ io->out.create_action = IVAL(req->in.body, 0x04);
+ io->out.create_time = smbcli_pull_nttime(req->in.body, 0x08);
+ io->out.access_time = smbcli_pull_nttime(req->in.body, 0x10);
+ io->out.write_time = smbcli_pull_nttime(req->in.body, 0x18);
+ io->out.change_time = smbcli_pull_nttime(req->in.body, 0x20);
+ io->out.alloc_size = BVAL(req->in.body, 0x28);
+ io->out.size = BVAL(req->in.body, 0x30);
+ io->out.file_attr = IVAL(req->in.body, 0x38);
+ io->out.reserved2 = IVAL(req->in.body, 0x3C);
+ smb2_pull_handle(req->in.body+0x40, &io->out.file.handle);
+ status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x50, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ status = smb2_create_blob_parse(mem_ctx, blob, &io->out.blobs);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ /* pull out the parsed blobs */
+ for (i=0;i<io->out.blobs.num_blobs;i++) {
+ if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_MXAC) == 0) {
+ /* TODO: this also contains a status field in
+ first 4 bytes */
+ if (io->out.blobs.blobs[i].data.length != 8) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ io->out.maximal_access = IVAL(io->out.blobs.blobs[i].data.data, 4);
+ }
+ if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_QFID) == 0) {
+ if (io->out.blobs.blobs[i].data.length != 32) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ memcpy(io->out.on_disk_id, io->out.blobs.blobs[i].data.data, 32);
+ }
+ }
+
+ data_blob_free(&blob);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync create request
+*/
+NTSTATUS smb2_create(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_create *io)
+{
+ struct smb2_request *req = smb2_create_send(tree, io);
+ return smb2_create_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/find.c b/source4/libcli/smb2/find.c
new file mode 100644
index 0000000000..8ebfd81bcd
--- /dev/null
+++ b/source4/libcli/smb2/find.c
@@ -0,0 +1,180 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client find calls
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a find request
+*/
+struct smb2_request *smb2_find_send(struct smb2_tree *tree, struct smb2_find *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_FIND, 0x20, true, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.level);
+ SCVAL(req->out.body, 0x03, io->in.continue_flags);
+ SIVAL(req->out.body, 0x04, io->in.file_index);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ status = smb2_push_o16s16_string(&req->out, 0x18, io->in.pattern);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SIVAL(req->out.body, 0x1C, io->in.max_response_size);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a find reply
+*/
+NTSTATUS smb2_find_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_find *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x08, true);
+
+ status = smb2_pull_o16s32_blob(&req->in, mem_ctx,
+ req->in.body+0x02, &io->out.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync find request
+*/
+NTSTATUS smb2_find(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ struct smb2_find *io)
+{
+ struct smb2_request *req = smb2_find_send(tree, io);
+ return smb2_find_recv(req, mem_ctx, io);
+}
+
+
+/*
+ a varient of smb2_find_recv that parses the resulting blob into
+ smb_search_data structures
+*/
+NTSTATUS smb2_find_level_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ uint8_t level, uint_t *count,
+ union smb_search_data **io)
+{
+ struct smb2_find f;
+ NTSTATUS status;
+ DATA_BLOB b;
+ enum smb_search_data_level smb_level;
+ uint_t next_ofs=0;
+
+ switch (level) {
+ case SMB2_FIND_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_DIRECTORY_INFO;
+ break;
+ case SMB2_FIND_FULL_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_FULL_DIRECTORY_INFO;
+ break;
+ case SMB2_FIND_BOTH_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
+ break;
+ case SMB2_FIND_NAME_INFO:
+ smb_level = RAW_SEARCH_DATA_NAME_INFO;
+ break;
+ case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO;
+ break;
+ case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO;
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ status = smb2_find_recv(req, mem_ctx, &f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ b = f.out.blob;
+ *io = NULL;
+ *count = 0;
+
+ do {
+ union smb_search_data *io2;
+
+ io2 = talloc_realloc(mem_ctx, *io, union smb_search_data, (*count)+1);
+ if (io2 == NULL) {
+ data_blob_free(&f.out.blob);
+ talloc_free(*io);
+ return NT_STATUS_NO_MEMORY;
+ }
+ *io = io2;
+
+ status = smb_raw_search_common(*io, smb_level, &b, (*io) + (*count),
+ &next_ofs, STR_UNICODE);
+
+ if (NT_STATUS_IS_OK(status) &&
+ next_ofs >= b.length) {
+ data_blob_free(&f.out.blob);
+ talloc_free(*io);
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ (*count)++;
+
+ b = data_blob_const(b.data+next_ofs, b.length - next_ofs);
+ } while (NT_STATUS_IS_OK(status) && next_ofs != 0);
+
+ data_blob_free(&f.out.blob);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ a varient of smb2_find that parses the resulting blob into
+ smb_search_data structures
+*/
+NTSTATUS smb2_find_level(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ struct smb2_find *f,
+ uint_t *count, union smb_search_data **io)
+{
+ struct smb2_request *req;
+
+ req = smb2_find_send(tree, f);
+ return smb2_find_level_recv(req, mem_ctx, f->in.level, count, io);
+}
diff --git a/source4/libcli/smb2/flush.c b/source4/libcli/smb2/flush.c
new file mode 100644
index 0000000000..577d1ba1ba
--- /dev/null
+++ b/source4/libcli/smb2/flush.c
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client flush handling
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a flush request
+*/
+struct smb2_request *smb2_flush_send(struct smb2_tree *tree, struct smb2_flush *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_FLUSH, 0x18, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, io->in.reserved1);
+ SIVAL(req->out.body, 0x04, io->in.reserved2);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a flush reply
+*/
+NTSTATUS smb2_flush_recv(struct smb2_request *req, struct smb2_flush *io)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+
+ io->out.reserved = SVAL(req->in.body, 0x02);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync flush request
+*/
+NTSTATUS smb2_flush(struct smb2_tree *tree, struct smb2_flush *io)
+{
+ struct smb2_request *req = smb2_flush_send(tree, io);
+ return smb2_flush_recv(req, io);
+}
diff --git a/source4/libcli/smb2/getinfo.c b/source4/libcli/smb2/getinfo.c
new file mode 100644
index 0000000000..14d911683e
--- /dev/null
+++ b/source4/libcli/smb2/getinfo.c
@@ -0,0 +1,220 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client getinfo calls
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a getinfo request
+*/
+struct smb2_request *smb2_getinfo_send(struct smb2_tree *tree, struct smb2_getinfo *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_GETINFO, 0x28, true,
+ io->in.blob.length);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.info_type);
+ SCVAL(req->out.body, 0x03, io->in.info_class);
+ SIVAL(req->out.body, 0x04, io->in.output_buffer_length);
+ SIVAL(req->out.body, 0x0C, io->in.reserved);
+ SIVAL(req->out.body, 0x08, io->in.input_buffer_length);
+ SIVAL(req->out.body, 0x10, io->in.additional_information);
+ SIVAL(req->out.body, 0x14, io->in.getinfo_flags);
+ smb2_push_handle(req->out.body+0x18, &io->in.file.handle);
+
+ /* this blob is used for quota queries */
+ status = smb2_push_o32s32_blob(&req->out, 0x08, io->in.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a getinfo reply
+*/
+NTSTATUS smb2_getinfo_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_getinfo *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x08, true);
+
+ status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync getinfo request
+*/
+NTSTATUS smb2_getinfo(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ struct smb2_getinfo *io)
+{
+ struct smb2_request *req = smb2_getinfo_send(tree, io);
+ return smb2_getinfo_recv(req, mem_ctx, io);
+}
+
+
+/*
+ map a generic info level to a SMB2 info level
+*/
+uint16_t smb2_getinfo_map_level(uint16_t level, uint8_t info_class)
+{
+ if (info_class == SMB2_GETINFO_FILE &&
+ level == RAW_FILEINFO_SEC_DESC) {
+ return SMB2_GETINFO_SECURITY;
+ }
+ if ((level & 0xFF) == info_class) {
+ return level;
+ } else if (level > 1000) {
+ return ((level-1000)<<8) | info_class;
+ }
+ DEBUG(0,("Unable to map SMB2 info level 0x%04x of class %d\n",
+ level, info_class));
+ return 0;
+}
+
+/*
+ level specific getinfo call - async send
+*/
+struct smb2_request *smb2_getinfo_file_send(struct smb2_tree *tree, union smb_fileinfo *io)
+{
+ struct smb2_getinfo b;
+ uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FILE);
+
+ if (smb2_level == 0) {
+ return NULL;
+ }
+
+ ZERO_STRUCT(b);
+ b.in.info_type = smb2_level & 0xFF;
+ b.in.info_class = smb2_level >> 8;
+ b.in.output_buffer_length = 0x10000;
+ b.in.input_buffer_length = 0;
+ b.in.file.handle = io->generic.in.file.handle;
+
+ if (io->generic.level == RAW_FILEINFO_SEC_DESC) {
+ b.in.additional_information = io->query_secdesc.in.secinfo_flags;
+ }
+ if (io->generic.level == RAW_FILEINFO_SMB2_ALL_EAS) {
+ b.in.getinfo_flags = io->all_eas.in.continue_flags;
+ }
+
+ return smb2_getinfo_send(tree, &b);
+}
+
+/*
+ recv a getinfo reply and parse the level info
+*/
+NTSTATUS smb2_getinfo_file_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *io)
+{
+ struct smb2_getinfo b;
+ NTSTATUS status;
+
+ status = smb2_getinfo_recv(req, mem_ctx, &b);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = smb_raw_fileinfo_passthru_parse(&b.out.blob, mem_ctx, io->generic.level, io);
+ data_blob_free(&b.out.blob);
+
+ return status;
+}
+
+/*
+ level specific getinfo call
+*/
+NTSTATUS smb2_getinfo_file(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *io)
+{
+ struct smb2_request *req = smb2_getinfo_file_send(tree, io);
+ return smb2_getinfo_file_recv(req, mem_ctx, io);
+}
+
+
+/*
+ level specific getinfo call - async send
+*/
+struct smb2_request *smb2_getinfo_fs_send(struct smb2_tree *tree, union smb_fsinfo *io)
+{
+ struct smb2_getinfo b;
+ uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FS);
+
+ if (smb2_level == 0) {
+ return NULL;
+ }
+
+ ZERO_STRUCT(b);
+ b.in.output_buffer_length = 0x10000;
+ b.in.file.handle = io->generic.handle;
+ b.in.info_type = smb2_level & 0xFF;
+ b.in.info_class = smb2_level >> 8;
+
+ return smb2_getinfo_send(tree, &b);
+}
+
+/*
+ recv a getinfo reply and parse the level info
+*/
+NTSTATUS smb2_getinfo_fs_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *io)
+{
+ struct smb2_getinfo b;
+ NTSTATUS status;
+
+ status = smb2_getinfo_recv(req, mem_ctx, &b);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = smb_raw_fsinfo_passthru_parse(b.out.blob, mem_ctx, io->generic.level, io);
+ data_blob_free(&b.out.blob);
+
+ return status;
+}
+
+/*
+ level specific getinfo call
+*/
+NTSTATUS smb2_getinfo_fs(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *io)
+{
+ struct smb2_request *req = smb2_getinfo_fs_send(tree, io);
+ return smb2_getinfo_fs_recv(req, mem_ctx, io);
+}
+
diff --git a/source4/libcli/smb2/ioctl.c b/source4/libcli/smb2/ioctl.c
new file mode 100644
index 0000000000..d81bca517f
--- /dev/null
+++ b/source4/libcli/smb2/ioctl.c
@@ -0,0 +1,109 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client ioctl call
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a ioctl request
+*/
+struct smb2_request *smb2_ioctl_send(struct smb2_tree *tree, struct smb2_ioctl *io)
+{
+ NTSTATUS status;
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_IOCTL, 0x38, true,
+ io->in.in.length+io->in.out.length);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, 0); /* pad */
+ SIVAL(req->out.body, 0x04, io->in.function);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ status = smb2_push_o32s32_blob(&req->out, 0x18, io->in.out);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SIVAL(req->out.body, 0x20, io->in.unknown2);
+
+ status = smb2_push_o32s32_blob(&req->out, 0x24, io->in.in);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SIVAL(req->out.body, 0x2C, io->in.max_response_size);
+ SBVAL(req->out.body, 0x30, io->in.flags);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a ioctl reply
+*/
+NTSTATUS smb2_ioctl_recv(struct smb2_request *req,
+ TALLOC_CTX *mem_ctx, struct smb2_ioctl *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x30, true);
+
+ io->out._pad = SVAL(req->in.body, 0x02);
+ io->out.function = IVAL(req->in.body, 0x04);
+ smb2_pull_handle(req->in.body+0x08, &io->out.file.handle);
+
+ status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x18, &io->out.in);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x20, &io->out.out);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ io->out.unknown2 = IVAL(req->in.body, 0x28);
+ io->out.unknown3 = IVAL(req->in.body, 0x2C);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync ioctl request
+*/
+NTSTATUS smb2_ioctl(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_ioctl *io)
+{
+ struct smb2_request *req = smb2_ioctl_send(tree, io);
+ return smb2_ioctl_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/keepalive.c b/source4/libcli/smb2/keepalive.c
new file mode 100644
index 0000000000..402b063e81
--- /dev/null
+++ b/source4/libcli/smb2/keepalive.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client keepalive handling
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a keepalive request
+*/
+struct smb2_request *smb2_keepalive_send(struct smb2_transport *transport)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init(transport, SMB2_OP_KEEPALIVE, 0x04, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a keepalive reply
+*/
+NTSTATUS smb2_keepalive_recv(struct smb2_request *req)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync keepalive request
+*/
+NTSTATUS smb2_keepalive(struct smb2_transport *transport)
+{
+ struct smb2_request *req = smb2_keepalive_send(transport);
+ return smb2_keepalive_recv(req);
+}
diff --git a/source4/libcli/smb2/lock.c b/source4/libcli/smb2/lock.c
new file mode 100644
index 0000000000..62c6e5dba7
--- /dev/null
+++ b/source4/libcli/smb2/lock.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client lock handling
+
+ Copyright (C) Stefan Metzmacher 2006
+
+ 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/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a lock request
+*/
+struct smb2_request *smb2_lock_send(struct smb2_tree *tree, struct smb2_lock *io)
+{
+ struct smb2_request *req;
+ int i;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_LOCK,
+ 24 + io->in.lock_count*24, false, 0);
+ if (req == NULL) return NULL;
+
+ /* this is quite bizarre - the spec says we must lie about the length! */
+ SSVAL(req->out.body, 0, 0x30);
+
+ SSVAL(req->out.body, 0x02, io->in.lock_count);
+ SIVAL(req->out.body, 0x04, io->in.reserved);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ for (i=0;i<io->in.lock_count;i++) {
+ SBVAL(req->out.body, 0x18 + i*24, io->in.locks[i].offset);
+ SBVAL(req->out.body, 0x20 + i*24, io->in.locks[i].length);
+ SIVAL(req->out.body, 0x28 + i*24, io->in.locks[i].flags);
+ SIVAL(req->out.body, 0x2C + i*24, io->in.locks[i].reserved);
+ }
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a lock reply
+*/
+NTSTATUS smb2_lock_recv(struct smb2_request *req, struct smb2_lock *io)
+{
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+
+ io->out.reserved = SVAL(req->in.body, 0x02);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync lock request
+*/
+NTSTATUS smb2_lock(struct smb2_tree *tree, struct smb2_lock *io)
+{
+ struct smb2_request *req = smb2_lock_send(tree, io);
+ return smb2_lock_recv(req, io);
+}
diff --git a/source4/libcli/smb2/logoff.c b/source4/libcli/smb2/logoff.c
new file mode 100644
index 0000000000..e3f83f27d8
--- /dev/null
+++ b/source4/libcli/smb2/logoff.c
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client logoff handling
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a logoff request
+*/
+struct smb2_request *smb2_logoff_send(struct smb2_session *session)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init(session->transport, SMB2_OP_LOGOFF, 0x04, false, 0);
+ if (req == NULL) return NULL;
+
+ req->session = session;
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, session->uid);
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a logoff reply
+*/
+NTSTATUS smb2_logoff_recv(struct smb2_request *req)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync logoff request
+*/
+NTSTATUS smb2_logoff(struct smb2_session *session)
+{
+ struct smb2_request *req = smb2_logoff_send(session);
+ return smb2_logoff_recv(req);
+}
diff --git a/source4/libcli/smb2/negprot.c b/source4/libcli/smb2/negprot.c
new file mode 100644
index 0000000000..c1f0cf0b24
--- /dev/null
+++ b/source4/libcli/smb2/negprot.c
@@ -0,0 +1,113 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client negprot handling
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "librpc/ndr/libndr.h"
+
+/*
+ send a negprot request
+*/
+struct smb2_request *smb2_negprot_send(struct smb2_transport *transport,
+ struct smb2_negprot *io)
+{
+ struct smb2_request *req;
+ uint16_t size = 0x24 + io->in.dialect_count*2;
+ enum ndr_err_code ndr_err;
+ int i;
+
+ req = smb2_request_init(transport, SMB2_OP_NEGPROT, size, false, 0);
+ if (req == NULL) return NULL;
+
+
+ SSVAL(req->out.body, 0x00, 0x24);
+ SSVAL(req->out.body, 0x02, io->in.dialect_count);
+ SSVAL(req->out.body, 0x04, io->in.security_mode);
+ SSVAL(req->out.body, 0x06, io->in.reserved);
+ SIVAL(req->out.body, 0x08, io->in.capabilities);
+ ndr_err = smbcli_push_guid(req->out.body, 0x0C, &io->in.client_guid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(req);
+ return NULL;
+ }
+ smbcli_push_nttime(req->out.body, 0x1C, io->in.start_time);
+ for (i=0;i<io->in.dialect_count;i++) {
+ SSVAL(req->out.body, 0x24 + i*2, io->in.dialects[i]);
+ }
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+/*
+ recv a negprot reply
+*/
+NTSTATUS smb2_negprot_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_negprot *io)
+{
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x40, true);
+
+ io->out.security_mode = SVAL(req->in.body, 0x02);
+ io->out.dialect_revision = SVAL(req->in.body, 0x04);
+ io->out.reserved = SVAL(req->in.body, 0x06);
+ ndr_err = smbcli_pull_guid(req->in.body, 0x08, &io->in.client_guid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ io->out.capabilities = IVAL(req->in.body, 0x18);
+ io->out.max_transact_size = IVAL(req->in.body, 0x1C);
+ io->out.max_read_size = IVAL(req->in.body, 0x20);
+ io->out.max_write_size = IVAL(req->in.body, 0x24);
+ io->out.system_time = smbcli_pull_nttime(req->in.body, 0x28);
+ io->out.server_start_time = smbcli_pull_nttime(req->in.body, 0x30);
+ io->out.reserved2 = IVAL(req->in.body, 0x3C);
+
+ status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x38, &io->out.secblob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync negprot request
+*/
+NTSTATUS smb2_negprot(struct smb2_transport *transport,
+ TALLOC_CTX *mem_ctx, struct smb2_negprot *io)
+{
+ struct smb2_request *req = smb2_negprot_send(transport, io);
+ return smb2_negprot_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/notify.c b/source4/libcli/smb2/notify.c
new file mode 100644
index 0000000000..ef7341cae8
--- /dev/null
+++ b/source4/libcli/smb2/notify.c
@@ -0,0 +1,114 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client notify calls
+
+ Copyright (C) Stefan Metzmacher 2006
+
+ 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/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a notify request
+*/
+struct smb2_request *smb2_notify_send(struct smb2_tree *tree, struct smb2_notify *io)
+{
+ struct smb2_request *req;
+ uint32_t old_timeout;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_NOTIFY, 0x20, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0x0030);
+
+ SSVAL(req->out.body, 0x02, io->in.recursive);
+ SIVAL(req->out.body, 0x04, io->in.buffer_size);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+ SIVAL(req->out.body, 0x18, io->in.completion_filter);
+ SIVAL(req->out.body, 0x1C, io->in.unknown);
+
+ old_timeout = req->transport->options.request_timeout;
+ req->transport->options.request_timeout = 0;
+ smb2_transport_send(req);
+ req->transport->options.request_timeout = old_timeout;
+
+ return req;
+}
+
+
+/*
+ recv a notify reply
+*/
+NTSTATUS smb2_notify_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_notify *io)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ uint32_t ofs, i;
+
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x08, true);
+
+ status = smb2_pull_o16s32_blob(&req->in, mem_ctx, req->in.body+0x02, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ io->out.changes = NULL;
+ io->out.num_changes = 0;
+
+ /* count them */
+ for (ofs=0; blob.length - ofs > 12; ) {
+ uint32_t next = IVAL(blob.data, ofs);
+ io->out.num_changes++;
+ if (next == 0 || (ofs + next) >= blob.length) break;
+ ofs += next;
+ }
+
+ /* allocate array */
+ io->out.changes = talloc_array(mem_ctx, struct notify_changes, io->out.num_changes);
+ if (!io->out.changes) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=ofs=0; i<io->out.num_changes; i++) {
+ io->out.changes[i].action = IVAL(blob.data, ofs+4);
+ smbcli_blob_pull_string(NULL, mem_ctx, &blob,
+ &io->out.changes[i].name,
+ ofs+8, ofs+12, STR_UNICODE);
+ ofs += IVAL(blob.data, ofs);
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync notify request
+*/
+NTSTATUS smb2_notify(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ struct smb2_notify *io)
+{
+ struct smb2_request *req = smb2_notify_send(tree, io);
+ return smb2_notify_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/read.c b/source4/libcli/smb2/read.c
new file mode 100644
index 0000000000..9d40e32a4d
--- /dev/null
+++ b/source4/libcli/smb2/read.c
@@ -0,0 +1,87 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client read call
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a read request
+*/
+struct smb2_request *smb2_read_send(struct smb2_tree *tree, struct smb2_read *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_READ, 0x30, true, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, 0); /* pad */
+ SCVAL(req->out.body, 0x03, 0); /* reserved */
+ SIVAL(req->out.body, 0x04, io->in.length);
+ SBVAL(req->out.body, 0x08, io->in.offset);
+ smb2_push_handle(req->out.body+0x10, &io->in.file.handle);
+ SIVAL(req->out.body, 0x20, io->in.min_count);
+ SIVAL(req->out.body, 0x24, io->in.channel);
+ SIVAL(req->out.body, 0x28, io->in.remaining);
+ SSVAL(req->out.body, 0x2C, io->in.channel_offset);
+ SSVAL(req->out.body, 0x2E, io->in.channel_length);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a read reply
+*/
+NTSTATUS smb2_read_recv(struct smb2_request *req,
+ TALLOC_CTX *mem_ctx, struct smb2_read *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x10, true);
+
+ status = smb2_pull_o16s32_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.data);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ io->out.remaining = IVAL(req->in.body, 0x08);
+ io->out.reserved = IVAL(req->in.body, 0x0C);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync read request
+*/
+NTSTATUS smb2_read(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_read *io)
+{
+ struct smb2_request *req = smb2_read_send(tree, io);
+ return smb2_read_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/request.c b/source4/libcli/smb2/request.c
new file mode 100644
index 0000000000..649a1db8d5
--- /dev/null
+++ b/source4/libcli/smb2/request.c
@@ -0,0 +1,737 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client request handling
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher 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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/* fill in the bufinfo */
+void smb2_setup_bufinfo(struct smb2_request *req)
+{
+ req->in.bufinfo.mem_ctx = req;
+ req->in.bufinfo.flags = BUFINFO_FLAG_UNICODE | BUFINFO_FLAG_SMB2;
+ req->in.bufinfo.align_base = req->in.buffer;
+ if (req->in.dynamic) {
+ req->in.bufinfo.data = req->in.dynamic;
+ req->in.bufinfo.data_size = req->in.body_size - req->in.body_fixed;
+ } else {
+ req->in.bufinfo.data = NULL;
+ req->in.bufinfo.data_size = 0;
+ }
+}
+
+
+/* destroy a request structure */
+static int smb2_request_destructor(struct smb2_request *req)
+{
+ if (req->transport) {
+ /* remove it from the list of pending requests (a null op if
+ its not in the list) */
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ return 0;
+}
+
+/*
+ initialise a smb2 request
+*/
+struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_t opcode,
+ uint16_t body_fixed_size, bool body_dynamic_present,
+ uint32_t body_dynamic_size)
+{
+ struct smb2_request *req;
+ uint64_t seqnum;
+
+ if (body_dynamic_present) {
+ if (body_dynamic_size == 0) {
+ body_dynamic_size = 1;
+ }
+ } else {
+ body_dynamic_size = 0;
+ }
+
+ req = talloc(transport, struct smb2_request);
+ if (req == NULL) return NULL;
+
+ seqnum = transport->seqnum++;
+ if (seqnum == UINT64_MAX) {
+ seqnum = transport->seqnum++;
+ }
+
+ req->state = SMB2_REQUEST_INIT;
+ req->transport = transport;
+ req->session = NULL;
+ req->tree = NULL;
+ req->seqnum = seqnum;
+ req->status = NT_STATUS_OK;
+ req->async.fn = NULL;
+ req->next = req->prev = NULL;
+
+ ZERO_STRUCT(req->cancel);
+ ZERO_STRUCT(req->in);
+
+ req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size;
+
+ req->out.allocated = req->out.size + body_dynamic_size;
+ req->out.buffer = talloc_array(req, uint8_t, req->out.allocated);
+ if (req->out.buffer == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.body = req->out.hdr + SMB2_HDR_BODY;
+ req->out.body_fixed= body_fixed_size;
+ req->out.body_size = body_fixed_size;
+ req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL);
+
+ SIVAL(req->out.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->out.hdr, SMB2_HDR_EPOCH, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0);
+ SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode);
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, req->seqnum);
+ SIVAL(req->out.hdr, SMB2_HDR_PID, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_TID, 0);
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, 0);
+ memset(req->out.hdr+SMB2_HDR_SIGNATURE, 0, 16);
+
+ /* set the length of the fixed body part and +1 if there's a dynamic part also */
+ SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
+
+ /*
+ * if we have a dynamic part, make sure the first byte
+ * which is always be part of the packet is initialized
+ */
+ if (body_dynamic_size) {
+ req->out.size += 1;
+ SCVAL(req->out.dynamic, 0, 0);
+ }
+
+ talloc_set_destructor(req, smb2_request_destructor);
+
+ return req;
+}
+
+/*
+ initialise a smb2 request for tree operations
+*/
+struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, uint16_t opcode,
+ uint16_t body_fixed_size, bool body_dynamic_present,
+ uint32_t body_dynamic_size)
+{
+ struct smb2_request *req = smb2_request_init(tree->session->transport, opcode,
+ body_fixed_size, body_dynamic_present,
+ body_dynamic_size);
+ if (req == NULL) return NULL;
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, tree->session->uid);
+ SIVAL(req->out.hdr, SMB2_HDR_TID, tree->tid);
+ req->session = tree->session;
+ req->tree = tree;
+
+ return req;
+}
+
+/* destroy a request structure and return final status */
+NTSTATUS smb2_request_destroy(struct smb2_request *req)
+{
+ NTSTATUS status;
+
+ /* this is the error code we give the application for when a
+ _send() call fails completely */
+ if (!req) return NT_STATUS_UNSUCCESSFUL;
+
+ if (req->state == SMB2_REQUEST_ERROR &&
+ NT_STATUS_IS_OK(req->status)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ } else {
+ status = req->status;
+ }
+
+ talloc_free(req);
+ return status;
+}
+
+/*
+ receive a response to a packet
+*/
+bool smb2_request_receive(struct smb2_request *req)
+{
+ /* req can be NULL when a send has failed. This eliminates lots of NULL
+ checks in each module */
+ if (!req) return false;
+
+ /* keep receiving packets until this one is replied to */
+ while (req->state <= SMB2_REQUEST_RECV) {
+ if (event_loop_once(req->transport->socket->event.ctx) != 0) {
+ return false;
+ }
+ }
+
+ return req->state == SMB2_REQUEST_DONE;
+}
+
+/* Return true if the last packet was in error */
+bool smb2_request_is_error(struct smb2_request *req)
+{
+ return NT_STATUS_IS_ERR(req->status);
+}
+
+/* Return true if the last packet was OK */
+bool smb2_request_is_ok(struct smb2_request *req)
+{
+ return NT_STATUS_IS_OK(req->status);
+}
+
+/*
+ check if a range in the reply body is out of bounds
+*/
+bool smb2_oob(struct smb2_request_buffer *buf, const uint8_t *ptr, size_t size)
+{
+ if (size == 0) {
+ /* zero bytes is never out of range */
+ return false;
+ }
+ /* be careful with wraparound! */
+ if ((uintptr_t)ptr < (uintptr_t)buf->body ||
+ (uintptr_t)ptr >= (uintptr_t)buf->body + buf->body_size ||
+ size > buf->body_size ||
+ (uintptr_t)ptr + size > (uintptr_t)buf->body + buf->body_size) {
+ return true;
+ }
+ return false;
+}
+
+size_t smb2_padding_size(uint32_t offset, size_t n)
+{
+ if ((offset & (n-1)) == 0) return 0;
+ return n - (offset & (n-1));
+}
+
+static size_t smb2_padding_fix(struct smb2_request_buffer *buf)
+{
+ if (buf->dynamic == (buf->body + buf->body_fixed)) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ grow a SMB2 buffer by the specified amount
+*/
+static NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase)
+{
+ size_t dynamic_ofs;
+ uint8_t *buffer_ptr;
+ uint32_t newsize = buf->size + increase;
+
+ /* a packet size should be limited a bit */
+ if (newsize >= 0x00FFFFFF) return NT_STATUS_MARSHALL_OVERFLOW;
+
+ if (newsize <= buf->allocated) return NT_STATUS_OK;
+
+ dynamic_ofs = buf->dynamic - buf->buffer;
+
+ buffer_ptr = talloc_realloc(buf, buf->buffer, uint8_t, newsize);
+ NT_STATUS_HAVE_NO_MEMORY(buffer_ptr);
+
+ buf->buffer = buffer_ptr;
+ buf->hdr = buf->buffer + NBT_HDR_SIZE;
+ buf->body = buf->hdr + SMB2_HDR_BODY;
+ buf->dynamic = buf->buffer + dynamic_ofs;
+ buf->allocated = newsize;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint16_t ofs/ uint16_t length/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_o16s16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint16_t ofs, size;
+ if (smb2_oob(buf, ptr, 4)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ofs = SVAL(ptr, 0);
+ size = SVAL(ptr, 2);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ push a uint16_t ofs/ uint16_t length/blob triple into a data blob
+ the ofs points to the start of the offset/length pair, and is relative
+ to the body start
+*/
+NTSTATUS smb2_push_o16s16_blob(struct smb2_request_buffer *buf,
+ uint16_t ofs, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ size_t offset;
+ size_t padding_length;
+ size_t padding_fix;
+ uint8_t *ptr = buf->body+ofs;
+
+ if (buf->dynamic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* we have only 16 bit for the size */
+ if (blob.length > 0xFFFF) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 4)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (blob.data == NULL) {
+ if (blob.length != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ SSVAL(ptr, 0, 0);
+ SSVAL(ptr, 2, 0);
+ return NT_STATUS_OK;
+ }
+
+ offset = buf->dynamic - buf->hdr;
+ padding_length = smb2_padding_size(offset, 2);
+ offset += padding_length;
+ padding_fix = smb2_padding_fix(buf);
+
+ SSVAL(ptr, 0, offset);
+ SSVAL(ptr, 2, blob.length);
+
+ status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memset(buf->dynamic, 0, padding_length);
+ buf->dynamic += padding_length;
+
+ memcpy(buf->dynamic, blob.data, blob.length);
+ buf->dynamic += blob.length;
+
+ buf->size += blob.length + padding_length - padding_fix;
+ buf->body_size += blob.length + padding_length;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ push a uint16_t ofs/ uint32_t length/blob triple into a data blob
+ the ofs points to the start of the offset/length pair, and is relative
+ to the body start
+*/
+NTSTATUS smb2_push_o16s32_blob(struct smb2_request_buffer *buf,
+ uint16_t ofs, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ size_t offset;
+ size_t padding_length;
+ size_t padding_fix;
+ uint8_t *ptr = buf->body+ofs;
+
+ if (buf->dynamic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 6)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (blob.data == NULL) {
+ if (blob.length != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ SSVAL(ptr, 0, 0);
+ SIVAL(ptr, 2, 0);
+ return NT_STATUS_OK;
+ }
+
+ offset = buf->dynamic - buf->hdr;
+ padding_length = smb2_padding_size(offset, 2);
+ offset += padding_length;
+ padding_fix = smb2_padding_fix(buf);
+
+ SSVAL(ptr, 0, offset);
+ SIVAL(ptr, 2, blob.length);
+
+ status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memset(buf->dynamic, 0, padding_length);
+ buf->dynamic += padding_length;
+
+ memcpy(buf->dynamic, blob.data, blob.length);
+ buf->dynamic += blob.length;
+
+ buf->size += blob.length + padding_length - padding_fix;
+ buf->body_size += blob.length + padding_length;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ push a uint32_t ofs/ uint32_t length/blob triple into a data blob
+ the ofs points to the start of the offset/length pair, and is relative
+ to the body start
+*/
+NTSTATUS smb2_push_o32s32_blob(struct smb2_request_buffer *buf,
+ uint32_t ofs, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ size_t offset;
+ size_t padding_length;
+ size_t padding_fix;
+ uint8_t *ptr = buf->body+ofs;
+
+ if (buf->dynamic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (blob.data == NULL) {
+ if (blob.length != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ SIVAL(ptr, 0, 0);
+ SIVAL(ptr, 4, 0);
+ return NT_STATUS_OK;
+ }
+
+ offset = buf->dynamic - buf->hdr;
+ padding_length = smb2_padding_size(offset, 8);
+ offset += padding_length;
+ padding_fix = smb2_padding_fix(buf);
+
+ SIVAL(ptr, 0, offset);
+ SIVAL(ptr, 4, blob.length);
+
+ status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memset(buf->dynamic, 0, padding_length);
+ buf->dynamic += padding_length;
+
+ memcpy(buf->dynamic, blob.data, blob.length);
+ buf->dynamic += blob.length;
+
+ buf->size += blob.length + padding_length - padding_fix;
+ buf->body_size += blob.length + padding_length;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ push a uint32_t length/ uint32_t ofs/blob triple into a data blob
+ the ofs points to the start of the length/offset pair, and is relative
+ to the body start
+*/
+NTSTATUS smb2_push_s32o32_blob(struct smb2_request_buffer *buf,
+ uint32_t ofs, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ size_t offset;
+ size_t padding_length;
+ size_t padding_fix;
+ uint8_t *ptr = buf->body+ofs;
+
+ if (buf->dynamic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (blob.data == NULL) {
+ if (blob.length != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ SIVAL(ptr, 0, 0);
+ SIVAL(ptr, 4, 0);
+ return NT_STATUS_OK;
+ }
+
+ offset = buf->dynamic - buf->hdr;
+ padding_length = smb2_padding_size(offset, 8);
+ offset += padding_length;
+ padding_fix = smb2_padding_fix(buf);
+
+ SIVAL(ptr, 0, blob.length);
+ SIVAL(ptr, 4, offset);
+
+ status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memset(buf->dynamic, 0, padding_length);
+ buf->dynamic += padding_length;
+
+ memcpy(buf->dynamic, blob.data, blob.length);
+ buf->dynamic += blob.length;
+
+ buf->size += blob.length + padding_length - padding_fix;
+ buf->body_size += blob.length + padding_length;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint16_t ofs/ uint32_t length/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_o16s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint16_t ofs;
+ uint32_t size;
+
+ if (smb2_oob(buf, ptr, 6)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ofs = SVAL(ptr, 0);
+ size = IVAL(ptr, 2);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint32_t ofs/ uint32_t length/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_o32s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint32_t ofs, size;
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ofs = IVAL(ptr, 0);
+ size = IVAL(ptr, 4);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint16_t ofs/ uint32_t length/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+
+ In this varient the uint16_t is padded by an extra 2 bytes, making
+ the size aligned on 4 byte boundary
+*/
+NTSTATUS smb2_pull_o16As32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint32_t ofs, size;
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ofs = SVAL(ptr, 0);
+ size = IVAL(ptr, 4);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint32_t length/ uint32_t ofs/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_s32o32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint32_t ofs, size;
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ size = IVAL(ptr, 0);
+ ofs = IVAL(ptr, 4);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint32_t length/ uint16_t ofs/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_s32o16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint32_t ofs, size;
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ size = IVAL(ptr, 0);
+ ofs = SVAL(ptr, 4);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a string in a uint16_t ofs/ uint16_t length/blob format
+ UTF-16 without termination
+*/
+NTSTATUS smb2_pull_o16s16_string(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx,
+ uint8_t *ptr, const char **str)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+ void *vstr;
+ bool ret;
+
+ status = smb2_pull_o16s16_blob(buf, mem_ctx, ptr, &blob);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (blob.data == NULL) {
+ *str = NULL;
+ return NT_STATUS_OK;
+ }
+
+ if (blob.length == 0) {
+ char *s;
+ s = talloc_strdup(mem_ctx, "");
+ NT_STATUS_HAVE_NO_MEMORY(s);
+ *str = s;
+ return NT_STATUS_OK;
+ }
+
+ ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX,
+ blob.data, blob.length, &vstr, NULL, false);
+ data_blob_free(&blob);
+ (*str) = (char *)vstr;
+ if (!ret) {
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ push a string in a uint16_t ofs/ uint16_t length/blob format
+ UTF-16 without termination
+*/
+NTSTATUS smb2_push_o16s16_string(struct smb2_request_buffer *buf,
+ uint16_t ofs, const char *str)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+ bool ret;
+
+ if (str == NULL) {
+ return smb2_push_o16s16_blob(buf, ofs, data_blob(NULL, 0));
+ }
+
+ if (*str == 0) {
+ blob.data = discard_const_p(uint8_t, str);
+ blob.length = 0;
+ return smb2_push_o16s16_blob(buf, ofs, blob);
+ }
+
+ ret = convert_string_talloc(buf->buffer, CH_UNIX, CH_UTF16,
+ str, strlen(str), (void **)&blob.data, &blob.length,
+ false);
+ if (!ret) {
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+
+ status = smb2_push_o16s16_blob(buf, ofs, blob);
+ data_blob_free(&blob);
+ return status;
+}
+
+/*
+ push a file handle into a buffer
+*/
+void smb2_push_handle(uint8_t *data, struct smb2_handle *h)
+{
+ SBVAL(data, 0, h->data[0]);
+ SBVAL(data, 8, h->data[1]);
+}
+
+/*
+ pull a file handle from a buffer
+*/
+void smb2_pull_handle(uint8_t *ptr, struct smb2_handle *h)
+{
+ h->data[0] = BVAL(ptr, 0);
+ h->data[1] = BVAL(ptr, 8);
+}
diff --git a/source4/libcli/smb2/session.c b/source4/libcli/smb2/session.c
new file mode 100644
index 0000000000..127bb9bcae
--- /dev/null
+++ b/source4/libcli/smb2/session.c
@@ -0,0 +1,274 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client session handling
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/composite/composite.h"
+#include "auth/gensec/gensec.h"
+
+/**
+ initialise a smb2_session structure
+ */
+struct smb2_session *smb2_session_init(struct smb2_transport *transport,
+ struct gensec_settings *settings,
+ TALLOC_CTX *parent_ctx, bool primary)
+{
+ struct smb2_session *session;
+ NTSTATUS status;
+
+ session = talloc_zero(parent_ctx, struct smb2_session);
+ if (!session) {
+ return NULL;
+ }
+ if (primary) {
+ session->transport = talloc_steal(session, transport);
+ } else {
+ session->transport = talloc_reference(session, transport);
+ }
+
+ /* prepare a gensec context for later use */
+ status = gensec_client_start(session, &session->gensec,
+ session->transport->socket->event.ctx,
+ settings);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(session);
+ return NULL;
+ }
+
+ gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
+
+ return session;
+}
+
+/**
+ send a session setup request
+*/
+struct smb2_request *smb2_session_setup_send(struct smb2_session *session,
+ struct smb2_session_setup *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+
+ req = smb2_request_init(session->transport, SMB2_OP_SESSSETUP,
+ 0x18, true, io->in.secblob.length);
+ if (req == NULL) return NULL;
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, session->uid);
+ SCVAL(req->out.body, 0x02, io->in.vc_number);
+ SCVAL(req->out.body, 0x03, io->in.security_mode);
+ SIVAL(req->out.body, 0x04, io->in.capabilities);
+ SIVAL(req->out.body, 0x08, io->in.channel);
+ SBVAL(req->out.body, 0x10, io->in.previous_sessionid);
+
+ req->session = session;
+
+ status = smb2_push_o16s16_blob(&req->out, 0x0C, io->in.secblob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/**
+ recv a session setup reply
+*/
+NTSTATUS smb2_session_setup_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_session_setup *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ (smb2_request_is_error(req) &&
+ !NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED))) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x08, true);
+
+ io->out.session_flags = SVAL(req->in.body, 0x02);
+ io->out.uid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
+
+ status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x04, &io->out.secblob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync session setup request
+*/
+NTSTATUS smb2_session_setup(struct smb2_session *session,
+ TALLOC_CTX *mem_ctx, struct smb2_session_setup *io)
+{
+ struct smb2_request *req = smb2_session_setup_send(session, io);
+ return smb2_session_setup_recv(req, mem_ctx, io);
+}
+
+
+struct smb2_session_state {
+ struct smb2_session_setup io;
+ struct smb2_request *req;
+ NTSTATUS gensec_status;
+};
+
+/*
+ handle continuations of the spnego session setup
+*/
+static void session_request_handler(struct smb2_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_session_state *state = talloc_get_type(c->private_data,
+ struct smb2_session_state);
+ struct smb2_session *session = req->session;
+
+ c->status = smb2_session_setup_recv(req, c, &state->io);
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
+ (NT_STATUS_IS_OK(c->status) &&
+ NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED))) {
+ NTSTATUS session_key_err;
+ DATA_BLOB session_key;
+ c->status = gensec_update(session->gensec, c,
+ state->io.out.secblob,
+ &state->io.in.secblob);
+ state->gensec_status = c->status;
+
+ session_key_err = gensec_session_key(session->gensec, &session_key);
+ if (NT_STATUS_IS_OK(session_key_err)) {
+ session->session_key = session_key;
+ }
+ }
+
+ session->uid = state->io.out.uid;
+
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ state->req = smb2_session_setup_send(session, &state->io);
+ if (state->req == NULL) {
+ composite_error(c, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ state->req->async.fn = session_request_handler;
+ state->req->async.private_data = c;
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ if (session->transport->signing_required) {
+ if (session->session_key.length == 0) {
+ DEBUG(0,("Wrong session key length %u for SMB2 signing\n",
+ (unsigned)session->session_key.length));
+ composite_error(c, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ session->signing_active = true;
+ }
+
+ composite_done(c);
+}
+
+/*
+ a composite function that does a full SPNEGO session setup
+ */
+struct composite_context *smb2_session_setup_spnego_send(struct smb2_session *session,
+ struct cli_credentials *credentials)
+{
+ struct composite_context *c;
+ struct smb2_session_state *state;
+
+ c = composite_create(session, session->transport->socket->event.ctx);
+ if (c == NULL) return NULL;
+
+ state = talloc(c, struct smb2_session_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ ZERO_STRUCT(state->io);
+ state->io.in.vc_number = 0;
+ if (session->transport->signing_required) {
+ state->io.in.security_mode =
+ SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ }
+ state->io.in.capabilities = 0;
+ state->io.in.channel = 0;
+ state->io.in.previous_sessionid = 0;
+
+ c->status = gensec_set_credentials(session->gensec, credentials);
+ if (!composite_is_ok(c)) return c;
+
+ c->status = gensec_set_target_hostname(session->gensec,
+ session->transport->socket->hostname);
+ if (!composite_is_ok(c)) return c;
+
+ c->status = gensec_set_target_service(session->gensec, "cifs");
+ if (!composite_is_ok(c)) return c;
+
+ c->status = gensec_start_mech_by_oid(session->gensec, GENSEC_OID_SPNEGO);
+ if (!composite_is_ok(c)) return c;
+
+ c->status = gensec_update(session->gensec, c,
+ session->transport->negotiate.secblob,
+ &state->io.in.secblob);
+ if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ composite_error(c, c->status);
+ return c;
+ }
+ state->gensec_status = c->status;
+
+ state->req = smb2_session_setup_send(session, &state->io);
+ composite_continue_smb2(c, state->req, session_request_handler, c);
+ return c;
+}
+
+/*
+ receive a composite session setup reply
+*/
+NTSTATUS smb2_session_setup_spnego_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+ status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+/*
+ sync version of smb2_session_setup_spnego
+*/
+NTSTATUS smb2_session_setup_spnego(struct smb2_session *session,
+ struct cli_credentials *credentials)
+{
+ struct composite_context *c = smb2_session_setup_spnego_send(session, credentials);
+ return smb2_session_setup_spnego_recv(c);
+}
diff --git a/source4/libcli/smb2/setinfo.c b/source4/libcli/smb2/setinfo.c
new file mode 100644
index 0000000000..69c0f45b63
--- /dev/null
+++ b/source4/libcli/smb2/setinfo.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client setinfo calls
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a setinfo request
+*/
+struct smb2_request *smb2_setinfo_send(struct smb2_tree *tree, struct smb2_setinfo *io)
+{
+ NTSTATUS status;
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_SETINFO, 0x20, true, io->in.blob.length);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, io->in.level);
+
+ status = smb2_push_s32o32_blob(&req->out, 0x04, io->in.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SIVAL(req->out.body, 0x0C, io->in.flags);
+ smb2_push_handle(req->out.body+0x10, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a setinfo reply
+*/
+NTSTATUS smb2_setinfo_recv(struct smb2_request *req)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x02, false);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync setinfo request
+*/
+NTSTATUS smb2_setinfo(struct smb2_tree *tree, struct smb2_setinfo *io)
+{
+ struct smb2_request *req = smb2_setinfo_send(tree, io);
+ return smb2_setinfo_recv(req);
+}
+
+/*
+ level specific file setinfo call - async send
+*/
+struct smb2_request *smb2_setinfo_file_send(struct smb2_tree *tree, union smb_setfileinfo *io)
+{
+ struct smb2_setinfo b;
+ uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FILE);
+ struct smb2_request *req;
+
+ if (smb2_level == 0) {
+ return NULL;
+ }
+
+ ZERO_STRUCT(b);
+ b.in.level = smb2_level;
+ b.in.file.handle = io->generic.in.file.handle;
+
+ /* change levels so the parsers know it is SMB2 */
+ if (io->generic.level == RAW_SFILEINFO_RENAME_INFORMATION) {
+ io->generic.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2;
+ }
+
+ if (!smb_raw_setfileinfo_passthru(tree, io->generic.level, io, &b.in.blob)) {
+ return NULL;
+ }
+
+ if (io->generic.level == RAW_SFILEINFO_SEC_DESC) {
+ b.in.flags = io->set_secdesc.in.secinfo_flags;
+ }
+
+ req = smb2_setinfo_send(tree, &b);
+ data_blob_free(&b.in.blob);
+ return req;
+}
+
+/*
+ level specific file setinfo call - sync
+*/
+NTSTATUS smb2_setinfo_file(struct smb2_tree *tree, union smb_setfileinfo *io)
+{
+ struct smb2_request *req = smb2_setinfo_file_send(tree, io);
+ return smb2_setinfo_recv(req);
+}
diff --git a/source4/libcli/smb2/signing.c b/source4/libcli/smb2/signing.c
new file mode 100644
index 0000000000..101fb00c12
--- /dev/null
+++ b/source4/libcli/smb2/signing.c
@@ -0,0 +1,117 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 Signing Code
+
+ Copyright (C) Andrew Tridgell <tridge@samba.org> 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/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "../lib/crypto/crypto.h"
+
+/*
+ sign an outgoing message
+ */
+NTSTATUS smb2_sign_message(struct smb2_request_buffer *buf, DATA_BLOB session_key)
+{
+ struct HMACSHA256Context m;
+ uint8_t res[32];
+ uint64_t session_id;
+
+ if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+ /* can't sign non-SMB2 messages */
+ return NT_STATUS_OK;
+ }
+
+ session_id = BVAL(buf->hdr, SMB2_HDR_SESSION_ID);
+ if (session_id == 0) {
+ /* we don't sign messages with a zero session_id. See
+ MS-SMB2 3.2.4.1.1 */
+ return NT_STATUS_OK;
+ }
+
+ if (session_key.length == 0) {
+ DEBUG(2,("Wrong session key length %u for SMB2 signing\n",
+ (unsigned)session_key.length));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ memset(buf->hdr + SMB2_HDR_SIGNATURE, 0, 16);
+
+ SIVAL(buf->hdr, SMB2_HDR_FLAGS, IVAL(buf->hdr, SMB2_HDR_FLAGS) | SMB2_HDR_FLAG_SIGNED);
+
+ ZERO_STRUCT(m);
+ hmac_sha256_init(session_key.data, MIN(session_key.length, 16), &m);
+ hmac_sha256_update(buf->buffer+NBT_HDR_SIZE, buf->size-NBT_HDR_SIZE, &m);
+ hmac_sha256_final(res, &m);
+ DEBUG(5,("signed SMB2 message of size %u\n", (unsigned)buf->size - NBT_HDR_SIZE));
+
+ memcpy(buf->hdr + SMB2_HDR_SIGNATURE, res, 16);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ check an incoming signature
+ */
+NTSTATUS smb2_check_signature(struct smb2_request_buffer *buf, DATA_BLOB session_key)
+{
+ uint64_t session_id;
+ struct HMACSHA256Context m;
+ uint8_t res[SHA256_DIGEST_LENGTH];
+ uint8_t sig[16];
+
+ if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+ /* can't check non-SMB2 messages */
+ return NT_STATUS_OK;
+ }
+
+ session_id = BVAL(buf->hdr, SMB2_HDR_SESSION_ID);
+ if (session_id == 0) {
+ /* don't sign messages with a zero session_id. See
+ MS-SMB2 3.2.4.1.1 */
+ return NT_STATUS_OK;
+ }
+
+ if (session_key.length == 0) {
+ /* we don't have the session key yet */
+ return NT_STATUS_OK;
+ }
+
+ memcpy(sig, buf->hdr+SMB2_HDR_SIGNATURE, 16);
+
+ memset(buf->hdr + SMB2_HDR_SIGNATURE, 0, 16);
+
+ ZERO_STRUCT(m);
+ hmac_sha256_init(session_key.data, MIN(session_key.length, 16), &m);
+ hmac_sha256_update(buf->hdr, buf->size-NBT_HDR_SIZE, &m);
+ hmac_sha256_final(res, &m);
+
+ memcpy(buf->hdr+SMB2_HDR_SIGNATURE, sig, 16);
+
+ if (memcmp(res, sig, 16) != 0) {
+ DEBUG(0,("Bad SMB2 signature for message of size %u\n",
+ (unsigned)buf->size-NBT_HDR_SIZE));
+ dump_data(0, sig, 16);
+ dump_data(0, res, 16);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h
new file mode 100644
index 0000000000..d1d5b842c3
--- /dev/null
+++ b/source4/libcli/smb2/smb2.h
@@ -0,0 +1,303 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client library header
+
+ Copyright (C) Andrew Tridgell 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_SMB2_SMB2_H__
+#define __LIBCLI_SMB2_SMB2_H__
+
+#include "libcli/raw/request.h"
+#include "libcli/raw/libcliraw.h"
+
+struct smb2_handle;
+
+/*
+ information returned from the negotiate process
+*/
+struct smb2_negotiate {
+ DATA_BLOB secblob;
+ NTTIME system_time;
+ NTTIME server_start_time;
+ uint16_t security_mode;
+};
+
+/* this is the context for the smb2 transport layer */
+struct smb2_transport {
+ /* socket level info */
+ struct smbcli_socket *socket;
+
+ struct smb2_negotiate negotiate;
+
+ /* next seqnum to allocate */
+ uint64_t seqnum;
+
+ /* a list of requests that are pending for receive on this
+ connection */
+ struct smb2_request *pending_recv;
+
+ /* context of the stream -> packet parser */
+ struct packet_context *packet;
+
+ /* an idle function - if this is defined then it will be
+ called once every period microseconds while we are waiting
+ for a packet */
+ struct {
+ void (*func)(struct smb2_transport *, void *);
+ void *private_data;
+ uint_t period;
+ } idle;
+
+ struct {
+ /* a oplock break request handler */
+ bool (*handler)(struct smb2_transport *transport,
+ const struct smb2_handle *handle,
+ uint8_t level, void *private_data);
+ /* private data passed to the oplock handler */
+ void *private_data;
+ } oplock;
+
+ struct smbcli_options options;
+
+ bool signing_required;
+};
+
+
+/*
+ SMB2 tree context
+*/
+struct smb2_tree {
+ struct smb2_session *session;
+ uint32_t tid;
+};
+
+/*
+ SMB2 session context
+*/
+struct smb2_session {
+ struct smb2_transport *transport;
+ struct gensec_security *gensec;
+ uint64_t uid;
+ DATA_BLOB session_key;
+ bool signing_active;
+};
+
+
+struct smb2_request_buffer {
+ /* the raw SMB2 buffer, including the 4 byte length header */
+ uint8_t *buffer;
+
+ /* the size of the raw buffer, including 4 byte header */
+ size_t size;
+
+ /* how much has been allocated - on reply the buffer is over-allocated to
+ prevent too many realloc() calls
+ */
+ size_t allocated;
+
+ /* the start of the SMB2 header - this is always buffer+4 */
+ uint8_t *hdr;
+
+ /* the packet body */
+ uint8_t *body;
+ size_t body_fixed;
+ size_t body_size;
+
+ /* this point to the next dynamic byte that can be used
+ * this will be moved when some dynamic data is pushed
+ */
+ uint8_t *dynamic;
+
+ /* this is used to range check and align strings and buffers */
+ struct request_bufinfo bufinfo;
+};
+
+
+/*
+ a client request moves between the following 4 states.
+*/
+enum smb2_request_state {SMB2_REQUEST_INIT, /* we are creating the request */
+ SMB2_REQUEST_RECV, /* we are waiting for a matching reply */
+ SMB2_REQUEST_DONE, /* the request is finished */
+ SMB2_REQUEST_ERROR}; /* a packet or transport level error has occurred */
+
+/* the context for a single SMB2 request */
+struct smb2_request {
+ /* allow a request to be part of a list of requests */
+ struct smb2_request *next, *prev;
+
+ /* each request is in one of 3 possible states */
+ enum smb2_request_state state;
+
+ struct smb2_transport *transport;
+ struct smb2_session *session;
+ struct smb2_tree *tree;
+
+ uint64_t seqnum;
+
+ struct {
+ bool do_cancel;
+ bool can_cancel;
+ uint32_t pending_id;
+ } cancel;
+
+ /* the NT status for this request. Set by packet receive code
+ or code detecting error. */
+ NTSTATUS status;
+
+ struct smb2_request_buffer in;
+ struct smb2_request_buffer out;
+
+ /* information on what to do with a reply when it is received
+ asyncronously. If this is not setup when a reply is received then
+ the reply is discarded
+
+ The private pointer is private to the caller of the client
+ library (the application), not private to the library
+ */
+ struct {
+ void (*fn)(struct smb2_request *);
+ void *private_data;
+ } async;
+};
+
+
+#define SMB2_MIN_SIZE 0x42
+#define SMB2_MIN_SIZE_NO_BODY 0x40
+
+/* offsets into header elements for a sync SMB2 request */
+#define SMB2_HDR_PROTOCOL_ID 0x00
+#define SMB2_HDR_LENGTH 0x04
+#define SMB2_HDR_EPOCH 0x06
+#define SMB2_HDR_STATUS 0x08
+#define SMB2_HDR_OPCODE 0x0c
+#define SMB2_HDR_CREDIT 0x0e
+#define SMB2_HDR_FLAGS 0x10
+#define SMB2_HDR_NEXT_COMMAND 0x14
+#define SMB2_HDR_MESSAGE_ID 0x18
+#define SMB2_HDR_PID 0x20
+#define SMB2_HDR_TID 0x24
+#define SMB2_HDR_SESSION_ID 0x28
+#define SMB2_HDR_SIGNATURE 0x30 /* 16 bytes */
+#define SMB2_HDR_BODY 0x40
+
+/* header flags */
+#define SMB2_HDR_FLAG_REDIRECT 0x01
+#define SMB2_HDR_FLAG_ASYNC 0x02
+#define SMB2_HDR_FLAG_CHAINED 0x04
+#define SMB2_HDR_FLAG_SIGNED 0x08
+#define SMB2_HDR_FLAG_DFS 0x10000000
+
+/* SMB2 opcodes */
+#define SMB2_OP_NEGPROT 0x00
+#define SMB2_OP_SESSSETUP 0x01
+#define SMB2_OP_LOGOFF 0x02
+#define SMB2_OP_TCON 0x03
+#define SMB2_OP_TDIS 0x04
+#define SMB2_OP_CREATE 0x05
+#define SMB2_OP_CLOSE 0x06
+#define SMB2_OP_FLUSH 0x07
+#define SMB2_OP_READ 0x08
+#define SMB2_OP_WRITE 0x09
+#define SMB2_OP_LOCK 0x0a
+#define SMB2_OP_IOCTL 0x0b
+#define SMB2_OP_CANCEL 0x0c
+#define SMB2_OP_KEEPALIVE 0x0d
+#define SMB2_OP_FIND 0x0e
+#define SMB2_OP_NOTIFY 0x0f
+#define SMB2_OP_GETINFO 0x10
+#define SMB2_OP_SETINFO 0x11
+#define SMB2_OP_BREAK 0x12
+
+#define SMB2_MAGIC 0x424D53FE /* 0xFE 'S' 'M' 'B' */
+
+/* the dialect we support */
+#define SMB2_DIALECT_REVISION 0x202
+
+/* SMB2 negotiate security_mode */
+#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x01
+#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x02
+
+/* SMB2 capabilities - only 1 so far. I'm sure more will be added */
+#define SMB2_CAP_DFS 0x0
+/* so we can spot new caps as added */
+#define SMB2_CAP_ALL SMB2_CAP_DFS
+
+/* SMB2 share flags */
+#define SMB2_SHAREFLAG_MANUAL_CACHING 0x0000
+#define SMB2_SHAREFLAG_AUTO_CACHING 0x0010
+#define SMB2_SHAREFLAG_VDO_CACHING 0x0020
+#define SMB2_SHAREFLAG_NO_CACHING 0x0030
+#define SMB2_SHAREFLAG_DFS 0x0001
+#define SMB2_SHAREFLAG_DFS_ROOT 0x0002
+#define SMB2_SHAREFLAG_RESTRICT_EXCLUSIVE_OPENS 0x0100
+#define SMB2_SHAREFLAG_FORCE_SHARED_DELETE 0x0200
+#define SMB2_SHAREFLAG_ALLOW_NAMESPACE_CACHING 0x0400
+#define SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM 0x0800
+#define SMB2_SHAREFLAG_ALL 0x0F33
+
+/* SMB2 create security flags */
+#define SMB2_SECURITY_DYNAMIC_TRACKING 0x01
+#define SMB2_SECURITY_EFFECTIVE_ONLY 0x02
+
+/* SMB2 requested oplock levels */
+#define SMB2_OPLOCK_LEVEL_NONE 0x00
+#define SMB2_OPLOCK_LEVEL_II 0x01
+#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
+#define SMB2_OPLOCK_LEVEL_BATCH 0x09
+
+/* SMB2 impersonation levels */
+#define SMB2_IMPERSONATION_ANONYMOUS 0x00
+#define SMB2_IMPERSONATION_IDENTIFICATION 0x01
+#define SMB2_IMPERSONATION_IMPERSONATION 0x02
+#define SMB2_IMPERSONATION_DELEGATE 0x03
+
+/* SMB2 create tags */
+#define SMB2_CREATE_TAG_EXTA "ExtA"
+#define SMB2_CREATE_TAG_MXAC "MxAc"
+#define SMB2_CREATE_TAG_SECD "SecD"
+#define SMB2_CREATE_TAG_DHNQ "DHnQ"
+#define SMB2_CREATE_TAG_DHNC "DHnC"
+#define SMB2_CREATE_TAG_ALSI "AlSi"
+#define SMB2_CREATE_TAG_TWRP "TWrp"
+#define SMB2_CREATE_TAG_QFID "QFid"
+
+/* SMB2 Create ignore some more create_options */
+#define SMB2_CREATE_OPTIONS_NOT_SUPPORTED_MASK (NTCREATEX_OPTIONS_TREE_CONNECTION | \
+ NTCREATEX_OPTIONS_OPFILTER)
+
+/*
+ check that a body has the expected size
+*/
+#define SMB2_CHECK_PACKET_RECV(req, size, dynamic) do { \
+ size_t is_size = req->in.body_size; \
+ uint16_t field_size = SVAL(req->in.body, 0); \
+ uint16_t want_size = ((dynamic)?(size)+1:(size)); \
+ if (is_size < (size)) { \
+ DEBUG(0,("%s: buffer too small 0x%x. Expected 0x%x\n", \
+ __location__, (unsigned)is_size, (unsigned)want_size)); \
+ return NT_STATUS_BUFFER_TOO_SMALL; \
+ }\
+ if (field_size != want_size) { \
+ DEBUG(0,("%s: unexpected fixed body size 0x%x. Expected 0x%x\n", \
+ __location__, (unsigned)field_size, (unsigned)want_size)); \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+} while (0)
+
+#endif
diff --git a/source4/libcli/smb2/smb2_calls.h b/source4/libcli/smb2/smb2_calls.h
new file mode 100644
index 0000000000..b89770fbe6
--- /dev/null
+++ b/source4/libcli/smb2/smb2_calls.h
@@ -0,0 +1,111 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client calls
+
+ Copyright (C) Andrew Tridgell 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/raw/interfaces.h"
+
+struct smb2_negprot {
+ struct {
+ uint16_t dialect_count; /* size of dialects array */
+ uint16_t security_mode; /* 0==signing disabled
+ 1==signing enabled */
+ uint16_t reserved;
+ uint32_t capabilities;
+ struct GUID client_guid;
+ NTTIME start_time;
+ uint16_t *dialects;
+ } in;
+ struct {
+ /* static body buffer 64 (0x40) bytes */
+ /* uint16_t buffer_code; 0x41 = 0x40 + 1 */
+ uint16_t security_mode; /* SMB2_NEGOTIATE_SIGNING_* */
+ uint16_t dialect_revision;
+ uint16_t reserved;
+ struct GUID server_guid;
+ uint32_t capabilities;
+ uint32_t max_transact_size;
+ uint32_t max_read_size;
+ uint32_t max_write_size;
+ NTTIME system_time;
+ NTTIME server_start_time;
+ /* uint16_t secblob_ofs */
+ /* uint16_t secblob_size */
+ uint32_t reserved2;
+ DATA_BLOB secblob;
+ } out;
+};
+
+/* getinfo classes */
+#define SMB2_GETINFO_FILE 0x01
+#define SMB2_GETINFO_FS 0x02
+#define SMB2_GETINFO_SECURITY 0x03
+#define SMB2_GETINFO_QUOTA 0x04
+
+#define SMB2_GETINFO_ADD_OWNER_SECURITY 0x01
+#define SMB2_GETINFO_ADD_GROUP_SECURITY 0x02
+#define SMB2_GETINFO_ADD_DACL_SECURITY 0x04
+#define SMB2_GETINFO_ADD_SACL_SECURITY 0x08
+#define SMB2_GETINFO_ADD_LABEL_SECURITY 0x10
+
+/* NOTE! the getinfo fs and file levels exactly match up with the
+ 'passthru' SMB levels, which are levels >= 1000. The SMB2 client
+ lib uses the names from the libcli/raw/ library */
+
+struct smb2_getinfo {
+ struct {
+ /* static body buffer 40 (0x28) bytes */
+ /* uint16_t buffer_code; 0x29 = 0x28 + 1 */
+ uint8_t info_type;
+ uint8_t info_class;
+ uint32_t output_buffer_length;
+ /* uint32_t input_buffer_offset; */
+ uint32_t reserved;
+ uint32_t input_buffer_length;
+ uint32_t additional_information; /* SMB2_GETINFO_ADD_* */
+ uint32_t getinfo_flags; /* level specific */
+ union smb_handle file;
+ DATA_BLOB blob;
+ } in;
+
+ struct {
+ /* static body buffer 8 (0x08) bytes */
+ /* uint16_t buffer_code; 0x09 = 0x08 + 1 */
+ /* uint16_t blob_ofs; */
+ /* uint16_t blob_size; */
+
+ /* dynamic body */
+ DATA_BLOB blob;
+ } out;
+};
+
+struct smb2_setinfo {
+ struct {
+ uint16_t level;
+ uint32_t flags;
+ union smb_handle file;
+ DATA_BLOB blob;
+ } in;
+};
+
+struct cli_credentials;
+struct tevent_context;
+struct resolve_context;
+struct gensec_settings;
+#include "libcli/smb2/smb2_proto.h"
diff --git a/source4/libcli/smb2/tcon.c b/source4/libcli/smb2/tcon.c
new file mode 100644
index 0000000000..ec7152b264
--- /dev/null
+++ b/source4/libcli/smb2/tcon.c
@@ -0,0 +1,112 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client tree handling
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ initialise a smb2_session structure
+ */
+struct smb2_tree *smb2_tree_init(struct smb2_session *session,
+ TALLOC_CTX *parent_ctx, bool primary)
+{
+ struct smb2_tree *tree;
+
+ tree = talloc_zero(parent_ctx, struct smb2_tree);
+ if (!session) {
+ return NULL;
+ }
+ if (primary) {
+ tree->session = talloc_steal(tree, session);
+ } else {
+ tree->session = talloc_reference(tree, session);
+ }
+ return tree;
+}
+
+/*
+ send a tree connect
+*/
+struct smb2_request *smb2_tree_connect_send(struct smb2_tree *tree,
+ struct smb2_tree_connect *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+
+ req = smb2_request_init(tree->session->transport, SMB2_OP_TCON,
+ 0x08, true, 0);
+ if (req == NULL) return NULL;
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, tree->session->uid);
+ req->session = tree->session;
+
+ SSVAL(req->out.body, 0x02, io->in.reserved);
+ status = smb2_push_o16s16_string(&req->out, 0x04, io->in.path);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a tree connect reply
+*/
+NTSTATUS smb2_tree_connect_recv(struct smb2_request *req, struct smb2_tree_connect *io)
+{
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x10, false);
+
+ io->out.tid = IVAL(req->in.hdr, SMB2_HDR_TID);
+
+ io->out.share_type = CVAL(req->in.body, 0x02);
+ io->out.reserved = CVAL(req->in.body, 0x03);
+ io->out.flags = IVAL(req->in.body, 0x04);
+ io->out.capabilities= IVAL(req->in.body, 0x08);
+ io->out.access_mask = IVAL(req->in.body, 0x0C);
+
+ if (io->out.capabilities & ~SMB2_CAP_ALL) {
+ DEBUG(0,("Unknown capabilities mask 0x%x\n", io->out.capabilities));
+ }
+ if (io->out.flags & ~SMB2_SHAREFLAG_ALL) {
+ DEBUG(0,("Unknown tcon shareflag 0x%x\n", io->out.flags));
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync tree connect request
+*/
+NTSTATUS smb2_tree_connect(struct smb2_tree *tree, struct smb2_tree_connect *io)
+{
+ struct smb2_request *req = smb2_tree_connect_send(tree, io);
+ return smb2_tree_connect_recv(req, io);
+}
diff --git a/source4/libcli/smb2/tdis.c b/source4/libcli/smb2/tdis.c
new file mode 100644
index 0000000000..5adad9dc6e
--- /dev/null
+++ b/source4/libcli/smb2/tdis.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client tdis handling
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a tdis request
+*/
+struct smb2_request *smb2_tdis_send(struct smb2_tree *tree)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_TDIS, 0x04, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a tdis reply
+*/
+NTSTATUS smb2_tdis_recv(struct smb2_request *req)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync tdis request
+*/
+NTSTATUS smb2_tdis(struct smb2_tree *tree)
+{
+ struct smb2_request *req = smb2_tdis_send(tree);
+ return smb2_tdis_recv(req);
+}
diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c
new file mode 100644
index 0000000000..e112544c62
--- /dev/null
+++ b/source4/libcli/smb2/transport.c
@@ -0,0 +1,417 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client transport context management functions
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "lib/stream/packet.h"
+#include "../lib/util/dlinklist.h"
+
+
+/*
+ an event has happened on the socket
+*/
+static void smb2_transport_event_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct smb2_transport *transport = talloc_get_type(private_data,
+ struct smb2_transport);
+ if (flags & EVENT_FD_READ) {
+ packet_recv(transport->packet);
+ return;
+ }
+ if (flags & EVENT_FD_WRITE) {
+ packet_queue_run(transport->packet);
+ }
+}
+
+/*
+ destroy a transport
+ */
+static int transport_destructor(struct smb2_transport *transport)
+{
+ smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
+ return 0;
+}
+
+
+/*
+ handle receive errors
+*/
+static void smb2_transport_error(void *private_data, NTSTATUS status)
+{
+ struct smb2_transport *transport = talloc_get_type(private_data,
+ struct smb2_transport);
+ smb2_transport_dead(transport, status);
+}
+
+static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob);
+
+/*
+ create a transport structure based on an established socket
+*/
+struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
+ TALLOC_CTX *parent_ctx,
+ struct smbcli_options *options)
+{
+ struct smb2_transport *transport;
+
+ transport = talloc_zero(parent_ctx, struct smb2_transport);
+ if (!transport) return NULL;
+
+ transport->socket = talloc_steal(transport, sock);
+ transport->options = *options;
+
+ /* setup the stream -> packet parser */
+ transport->packet = packet_init(transport);
+ if (transport->packet == NULL) {
+ talloc_free(transport);
+ return NULL;
+ }
+ packet_set_private(transport->packet, transport);
+ packet_set_socket(transport->packet, transport->socket->sock);
+ packet_set_callback(transport->packet, smb2_transport_finish_recv);
+ packet_set_full_request(transport->packet, packet_full_request_nbt);
+ packet_set_error_handler(transport->packet, smb2_transport_error);
+ packet_set_event_context(transport->packet, transport->socket->event.ctx);
+ packet_set_nofree(transport->packet);
+
+ /* take over event handling from the socket layer - it only
+ handles events up until we are connected */
+ talloc_free(transport->socket->event.fde);
+ transport->socket->event.fde = event_add_fd(transport->socket->event.ctx,
+ transport->socket,
+ socket_get_fd(transport->socket->sock),
+ EVENT_FD_READ,
+ smb2_transport_event_handler,
+ transport);
+
+ packet_set_fde(transport->packet, transport->socket->event.fde);
+ packet_set_serialise(transport->packet);
+
+ talloc_set_destructor(transport, transport_destructor);
+
+ return transport;
+}
+
+/*
+ mark the transport as dead
+*/
+void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
+{
+ smbcli_sock_dead(transport->socket);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ /* kill all pending receives */
+ while (transport->pending_recv) {
+ struct smb2_request *req = transport->pending_recv;
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = status;
+ DLIST_REMOVE(transport->pending_recv, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ }
+}
+
+static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport,
+ const DATA_BLOB *blob)
+{
+ uint8_t *hdr;
+ uint16_t opcode;
+
+ hdr = blob->data+NBT_HDR_SIZE;
+
+ if (blob->length < (SMB2_MIN_SIZE+0x18)) {
+ DEBUG(1,("Discarding smb2 oplock reply of size %u\n",
+ (unsigned)blob->length));
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ opcode = SVAL(hdr, SMB2_HDR_OPCODE);
+
+ if (opcode != SMB2_OP_BREAK) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (transport->oplock.handler) {
+ uint8_t *body = hdr+SMB2_HDR_BODY;
+ struct smb2_handle h;
+ uint8_t level;
+
+ level = CVAL(body, 0x02);
+ smb2_pull_handle(body+0x08, &h);
+
+ transport->oplock.handler(transport, &h, level,
+ transport->oplock.private_data);
+ } else {
+ DEBUG(5,("Got SMB2 oplock break with no handler\n"));
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ we have a full request in our receive buffer - match it to a pending request
+ and process
+ */
+static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob)
+{
+ struct smb2_transport *transport = talloc_get_type(private_data,
+ struct smb2_transport);
+ uint8_t *buffer, *hdr;
+ int len;
+ struct smb2_request *req = NULL;
+ uint64_t seqnum;
+ uint32_t flags;
+ uint16_t buffer_code;
+ uint32_t dynamic_size;
+ uint32_t i;
+ NTSTATUS status;
+
+ buffer = blob.data;
+ len = blob.length;
+
+ hdr = buffer+NBT_HDR_SIZE;
+
+ if (len < SMB2_MIN_SIZE) {
+ DEBUG(1,("Discarding smb2 reply of size %d\n", len));
+ goto error;
+ }
+
+ flags = IVAL(hdr, SMB2_HDR_FLAGS);
+ seqnum = BVAL(hdr, SMB2_HDR_MESSAGE_ID);
+
+ /* see MS-SMB2 3.2.5.19 */
+ if (seqnum == UINT64_MAX) {
+ return smb2_handle_oplock_break(transport, &blob);
+ }
+
+ /* match the incoming request against the list of pending requests */
+ for (req=transport->pending_recv; req; req=req->next) {
+ if (req->seqnum == seqnum) break;
+ }
+
+ if (!req) {
+ DEBUG(1,("Discarding unmatched reply with seqnum 0x%llx op %d\n",
+ (long long)seqnum, SVAL(hdr, SMB2_HDR_OPCODE)));
+ goto error;
+ }
+
+ /* fill in the 'in' portion of the matching request */
+ req->in.buffer = buffer;
+ talloc_steal(req, buffer);
+ req->in.size = len;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = hdr;
+ req->in.body = hdr+SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
+ req->status = NT_STATUS(IVAL(hdr, SMB2_HDR_STATUS));
+
+ if ((flags & SMB2_HDR_FLAG_ASYNC) &&
+ NT_STATUS_EQUAL(req->status, STATUS_PENDING)) {
+ req->cancel.can_cancel = true;
+ req->cancel.pending_id = IVAL(hdr, SMB2_HDR_PID);
+ for (i=0; i< req->cancel.do_cancel; i++) {
+ smb2_cancel(req);
+ }
+ talloc_free(buffer);
+ return NT_STATUS_OK;
+ }
+
+ if (req->session && req->session->signing_active) {
+ status = smb2_check_signature(&req->in,
+ req->session->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* the spec says to ignore packets with a bad signature */
+ talloc_free(buffer);
+ return status;
+ }
+ }
+
+ buffer_code = SVAL(req->in.body, 0);
+ req->in.body_fixed = (buffer_code & ~1);
+ req->in.dynamic = NULL;
+ dynamic_size = req->in.body_size - req->in.body_fixed;
+ if (dynamic_size != 0 && (buffer_code & 1)) {
+ req->in.dynamic = req->in.body + req->in.body_fixed;
+ if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
+ DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n",
+ dynamic_size));
+ goto error;
+ }
+ }
+
+ smb2_setup_bufinfo(req);
+
+ DEBUG(2, ("SMB2 RECV seqnum=0x%llx\n", (long long)req->seqnum));
+ dump_data(5, req->in.body, req->in.body_size);
+
+ /* if this request has an async handler then call that to
+ notify that the reply has been received. This might destroy
+ the request so it must happen last */
+ DLIST_REMOVE(transport->pending_recv, req);
+ req->state = SMB2_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return NT_STATUS_OK;
+
+error:
+ dump_data(5, buffer, len);
+ if (req) {
+ DLIST_REMOVE(transport->pending_recv, req);
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ } else {
+ talloc_free(buffer);
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ handle timeouts of individual smb requests
+*/
+static void smb2_timeout_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct smb2_request *req = talloc_get_type(private_data, struct smb2_request);
+
+ if (req->state == SMB2_REQUEST_RECV) {
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ req->status = NT_STATUS_IO_TIMEOUT;
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+
+/*
+ destroy a request
+*/
+static int smb2_request_destructor(struct smb2_request *req)
+{
+ if (req->state == SMB2_REQUEST_RECV) {
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ return 0;
+}
+
+
+/*
+ put a request into the send queue
+*/
+void smb2_transport_send(struct smb2_request *req)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ _smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+
+ DEBUG(2, ("SMB2 send seqnum=0x%llx\n", (long long)req->seqnum));
+ dump_data(5, req->out.body, req->out.body_size);
+
+ /* check if the transport is dead */
+ if (req->transport->socket->sock == NULL) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = NT_STATUS_NET_WRITE_FAULT;
+ return;
+ }
+
+ /* possibly sign the message */
+ if (req->session && req->session->signing_active) {
+ status = smb2_sign_message(&req->out, req->session->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = status;
+ return;
+ }
+ }
+
+ blob = data_blob_const(req->out.buffer, req->out.size);
+ status = packet_send(req->transport->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = status;
+ return;
+ }
+
+ req->state = SMB2_REQUEST_RECV;
+ DLIST_ADD(req->transport->pending_recv, req);
+
+ /* add a timeout */
+ if (req->transport->options.request_timeout) {
+ event_add_timed(req->transport->socket->event.ctx, req,
+ timeval_current_ofs(req->transport->options.request_timeout, 0),
+ smb2_timeout_handler, req);
+ }
+
+ talloc_set_destructor(req, smb2_request_destructor);
+}
+
+static void idle_handler(struct tevent_context *ev,
+ struct tevent_timer *te, struct timeval t, void *private_data)
+{
+ struct smb2_transport *transport = talloc_get_type(private_data,
+ struct smb2_transport);
+ struct timeval next = timeval_add(&t, 0, transport->idle.period);
+ transport->socket->event.te = event_add_timed(transport->socket->event.ctx,
+ transport,
+ next,
+ idle_handler, transport);
+ transport->idle.func(transport, transport->idle.private_data);
+}
+
+/*
+ setup the idle handler for a transport
+ the period is in microseconds
+*/
+void smb2_transport_idle_handler(struct smb2_transport *transport,
+ void (*idle_func)(struct smb2_transport *, void *),
+ uint64_t period,
+ void *private_data)
+{
+ transport->idle.func = idle_func;
+ transport->idle.private_data = private_data;
+ transport->idle.period = period;
+
+ if (transport->socket->event.te != NULL) {
+ talloc_free(transport->socket->event.te);
+ }
+
+ transport->socket->event.te = event_add_timed(transport->socket->event.ctx,
+ transport,
+ timeval_current_ofs(0, period),
+ idle_handler, transport);
+}
diff --git a/source4/libcli/smb2/util.c b/source4/libcli/smb2/util.c
new file mode 100644
index 0000000000..a360d8fbdf
--- /dev/null
+++ b/source4/libcli/smb2/util.c
@@ -0,0 +1,216 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client utility functions
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/*
+ simple close wrapper with SMB2
+*/
+NTSTATUS smb2_util_close(struct smb2_tree *tree, struct smb2_handle h)
+{
+ struct smb2_close c;
+
+ ZERO_STRUCT(c);
+ c.in.file.handle = h;
+
+ return smb2_close(tree, &c);
+}
+
+/*
+ unlink a file with SMB2
+*/
+NTSTATUS smb2_util_unlink(struct smb2_tree *tree, const char *fname)
+{
+ union smb_unlink io;
+
+ ZERO_STRUCT(io);
+ io.unlink.in.pattern = fname;
+
+ return smb2_composite_unlink(tree, &io);
+}
+
+
+/*
+ rmdir with SMB2
+*/
+NTSTATUS smb2_util_rmdir(struct smb2_tree *tree, const char *dname)
+{
+ struct smb_rmdir io;
+
+ ZERO_STRUCT(io);
+ io.in.path = dname;
+
+ return smb2_composite_rmdir(tree, &io);
+}
+
+
+/*
+ mkdir with SMB2
+*/
+NTSTATUS smb2_util_mkdir(struct smb2_tree *tree, const char *dname)
+{
+ union smb_mkdir io;
+
+ ZERO_STRUCT(io);
+ io.mkdir.level = RAW_MKDIR_MKDIR;
+ io.mkdir.in.path = dname;
+
+ return smb2_composite_mkdir(tree, &io);
+}
+
+
+/*
+ set file attribute with SMB2
+*/
+NTSTATUS smb2_util_setatr(struct smb2_tree *tree, const char *name, uint32_t attrib)
+{
+ union smb_setfileinfo io;
+
+ ZERO_STRUCT(io);
+ io.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
+ io.basic_info.in.file.path = name;
+ io.basic_info.in.attrib = attrib;
+
+ return smb2_composite_setpathinfo(tree, &io);
+}
+
+
+
+
+/*
+ recursively descend a tree deleting all files
+ returns the number of files deleted, or -1 on error
+*/
+int smb2_deltree(struct smb2_tree *tree, const char *dname)
+{
+ NTSTATUS status;
+ uint32_t total_deleted = 0;
+ uint_t count, i;
+ union smb_search_data *list;
+ TALLOC_CTX *tmp_ctx = talloc_new(tree);
+ struct smb2_find f;
+ struct smb2_create create_parm;
+
+ /* it might be a file */
+ status = smb2_util_unlink(tree, dname);
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return 1;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) {
+ talloc_free(tmp_ctx);
+ return 0;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ status = smb2_util_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL);
+ status = smb2_util_unlink(tree, dname);
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return 1;
+ }
+
+ ZERO_STRUCT(create_parm);
+ create_parm.in.desired_access = SEC_FILE_READ_DATA;
+ create_parm.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ create_parm.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ create_parm.in.create_disposition = NTCREATEX_DISP_OPEN;
+ create_parm.in.fname = dname;
+
+ status = smb2_create(tree, tmp_ctx, &create_parm);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(2,("Failed to open %s - %s\n", dname, nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+
+ ZERO_STRUCT(f);
+ f.in.file.handle = create_parm.out.file.handle;
+ f.in.max_response_size = 0x10000;
+ f.in.level = SMB2_FIND_NAME_INFO;
+ f.in.pattern = "*";
+
+ status = smb2_find_level(tree, tmp_ctx, &f, &count, &list);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(2,("Failed to list %s - %s\n",
+ dname, nt_errstr(status)));
+ smb2_util_close(tree, create_parm.out.file.handle);
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ for (i=0;i<count;i++) {
+ char *name;
+ if (strcmp(".", list[i].name_info.name.s) == 0 ||
+ strcmp("..", list[i].name_info.name.s) == 0) {
+ continue;
+ }
+ name = talloc_asprintf(tmp_ctx, "%s\\%s", dname, list[i].name_info.name.s);
+ status = smb2_util_unlink(tree, name);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ status = smb2_util_setatr(tree, name, FILE_ATTRIBUTE_NORMAL);
+ status = smb2_util_unlink(tree, name);
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ int ret;
+ ret = smb2_deltree(tree, name);
+ if (ret > 0) total_deleted += ret;
+ }
+ talloc_free(name);
+ if (NT_STATUS_IS_OK(status)) {
+ total_deleted++;
+ }
+ }
+
+ smb2_util_close(tree, create_parm.out.file.handle);
+
+ status = smb2_util_rmdir(tree, dname);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ status = smb2_util_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL);
+ status = smb2_util_rmdir(tree, dname);
+ }
+
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(2,("Failed to delete %s - %s\n",
+ dname, nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return total_deleted;
+}
diff --git a/source4/libcli/smb2/write.c b/source4/libcli/smb2/write.c
new file mode 100644
index 0000000000..bc283370d7
--- /dev/null
+++ b/source4/libcli/smb2/write.c
@@ -0,0 +1,81 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client write call
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a write request
+*/
+struct smb2_request *smb2_write_send(struct smb2_tree *tree, struct smb2_write *io)
+{
+ NTSTATUS status;
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_WRITE, 0x30, true, io->in.data.length);
+ if (req == NULL) return NULL;
+
+ status = smb2_push_o16s32_blob(&req->out, 0x02, io->in.data);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SBVAL(req->out.body, 0x08, io->in.offset);
+ smb2_push_handle(req->out.body+0x10, &io->in.file.handle);
+
+ SBVAL(req->out.body, 0x20, io->in.unknown1);
+ SBVAL(req->out.body, 0x28, io->in.unknown2);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a write reply
+*/
+NTSTATUS smb2_write_recv(struct smb2_request *req, struct smb2_write *io)
+{
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x10, true);
+
+ io->out._pad = SVAL(req->in.body, 0x02);
+ io->out.nwritten = IVAL(req->in.body, 0x04);
+ io->out.unknown1 = BVAL(req->in.body, 0x08);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync write request
+*/
+NTSTATUS smb2_write(struct smb2_tree *tree, struct smb2_write *io)
+{
+ struct smb2_request *req = smb2_write_send(tree, io);
+ return smb2_write_recv(req, io);
+}
diff --git a/source4/libcli/smb_composite/appendacl.c b/source4/libcli/smb_composite/appendacl.c
new file mode 100644
index 0000000000..69ed62a106
--- /dev/null
+++ b/source4/libcli/smb_composite/appendacl.c
@@ -0,0 +1,313 @@
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/security/security.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/* the stages of this call */
+enum appendacl_stage {APPENDACL_OPENPATH, APPENDACL_GET,
+ APPENDACL_SET, APPENDACL_GETAGAIN, APPENDACL_CLOSEPATH};
+
+static void appendacl_handler(struct smbcli_request *req);
+
+struct appendacl_state {
+ enum appendacl_stage stage;
+ struct smb_composite_appendacl *io;
+
+ union smb_open *io_open;
+ union smb_setfileinfo *io_setfileinfo;
+ union smb_fileinfo *io_fileinfo;
+
+ struct smbcli_request *req;
+};
+
+
+static NTSTATUS appendacl_open(struct composite_context *c,
+ struct smb_composite_appendacl *io)
+{
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+ struct smbcli_tree *tree = state->req->tree;
+ NTSTATUS status;
+
+ status = smb_raw_open_recv(state->req, c, state->io_open);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* setup structures for getting fileinfo */
+ state->io_fileinfo = talloc(c, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_fileinfo);
+
+ state->io_fileinfo->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ state->io_fileinfo->query_secdesc.in.file.fnum = state->io_open->ntcreatex.out.file.fnum;
+ state->io_fileinfo->query_secdesc.in.secinfo_flags = SECINFO_DACL;
+
+ state->req = smb_raw_fileinfo_send(tree, state->io_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* set the handler */
+ state->req->async.fn = appendacl_handler;
+ state->req->async.private_data = c;
+ state->stage = APPENDACL_GET;
+
+ talloc_free (state->io_open);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS appendacl_get(struct composite_context *c,
+ struct smb_composite_appendacl *io)
+{
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+ struct smbcli_tree *tree = state->req->tree;
+ int i;
+ NTSTATUS status;
+
+ status = smb_raw_fileinfo_recv(state->req, state->io_fileinfo, state->io_fileinfo);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* setup structures for setting fileinfo */
+ state->io_setfileinfo = talloc(c, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_setfileinfo);
+
+ state->io_setfileinfo->set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ state->io_setfileinfo->set_secdesc.in.file.fnum = state->io_fileinfo->query_secdesc.in.file.fnum;
+
+ state->io_setfileinfo->set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ state->io_setfileinfo->set_secdesc.in.sd = state->io_fileinfo->query_secdesc.out.sd;
+ talloc_steal(state->io_setfileinfo, state->io_setfileinfo->set_secdesc.in.sd);
+
+ /* append all aces from io->in.sd->dacl to new security descriptor */
+ if (io->in.sd->dacl != NULL) {
+ for (i = 0; i < io->in.sd->dacl->num_aces; i++) {
+ security_descriptor_dacl_add(state->io_setfileinfo->set_secdesc.in.sd,
+ &(io->in.sd->dacl->aces[i]));
+ }
+ }
+
+ status = smb_raw_setfileinfo(tree, state->io_setfileinfo);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->req = smb_raw_setfileinfo_send(tree, state->io_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call handler when done setting new security descriptor on file */
+ state->req->async.fn = appendacl_handler;
+ state->req->async.private_data = c;
+ state->stage = APPENDACL_SET;
+
+ talloc_free (state->io_fileinfo);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS appendacl_set(struct composite_context *c,
+ struct smb_composite_appendacl *io)
+{
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+ struct smbcli_tree *tree = state->req->tree;
+ NTSTATUS status;
+
+ status = smbcli_request_simple_recv(state->req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* setup structures for getting fileinfo */
+ state->io_fileinfo = talloc(c, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_fileinfo);
+
+
+ state->io_fileinfo->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ state->io_fileinfo->query_secdesc.in.file.fnum = state->io_setfileinfo->set_secdesc.in.file.fnum;
+ state->io_fileinfo->query_secdesc.in.secinfo_flags = SECINFO_DACL;
+
+ state->req = smb_raw_fileinfo_send(tree, state->io_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* set the handler */
+ state->req->async.fn = appendacl_handler;
+ state->req->async.private_data = c;
+ state->stage = APPENDACL_GETAGAIN;
+
+ talloc_free (state->io_setfileinfo);
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS appendacl_getagain(struct composite_context *c,
+ struct smb_composite_appendacl *io)
+{
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+ struct smbcli_tree *tree = state->req->tree;
+ union smb_close *io_close;
+ NTSTATUS status;
+
+ status = smb_raw_fileinfo_recv(state->req, c, state->io_fileinfo);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ io->out.sd = state->io_fileinfo->query_secdesc.out.sd;
+
+ /* setup structures for close */
+ io_close = talloc(c, union smb_close);
+ NT_STATUS_HAVE_NO_MEMORY(io_close);
+
+ io_close->close.level = RAW_CLOSE_CLOSE;
+ io_close->close.in.file.fnum = state->io_fileinfo->query_secdesc.in.file.fnum;
+ io_close->close.in.write_time = 0;
+
+ state->req = smb_raw_close_send(tree, io_close);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler */
+ state->req->async.fn = appendacl_handler;
+ state->req->async.private_data = c;
+ state->stage = APPENDACL_CLOSEPATH;
+
+ talloc_free (state->io_fileinfo);
+
+ return NT_STATUS_OK;
+}
+
+
+
+static NTSTATUS appendacl_close(struct composite_context *c,
+ struct smb_composite_appendacl *io)
+{
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+ NTSTATUS status;
+
+ status = smbcli_request_simple_recv(state->req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ c->state = COMPOSITE_STATE_DONE;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ handler for completion of a sub-request in appendacl
+*/
+static void appendacl_handler(struct smbcli_request *req)
+{
+ struct composite_context *c = (struct composite_context *)req->async.private_data;
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+
+ /* when this handler is called, the stage indicates what
+ call has just finished */
+ switch (state->stage) {
+ case APPENDACL_OPENPATH:
+ c->status = appendacl_open(c, state->io);
+ break;
+
+ case APPENDACL_GET:
+ c->status = appendacl_get(c, state->io);
+ break;
+
+ case APPENDACL_SET:
+ c->status = appendacl_set(c, state->io);
+ break;
+
+ case APPENDACL_GETAGAIN:
+ c->status = appendacl_getagain(c, state->io);
+ break;
+
+ case APPENDACL_CLOSEPATH:
+ c->status = appendacl_close(c, state->io);
+ break;
+ }
+
+ /* We should get here if c->state >= SMBCLI_REQUEST_DONE */
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (c->state >= COMPOSITE_STATE_DONE &&
+ c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+
+/*
+ composite appendacl call - does an open followed by a number setfileinfo,
+ after that new acls are read with fileinfo, followed by a close
+*/
+struct composite_context *smb_composite_appendacl_send(struct smbcli_tree *tree,
+ struct smb_composite_appendacl *io)
+{
+ struct composite_context *c;
+ struct appendacl_state *state;
+
+ c = talloc_zero(tree, struct composite_context);
+ if (c == NULL) goto failed;
+
+ state = talloc(c, struct appendacl_state);
+ if (state == NULL) goto failed;
+
+ state->io = io;
+
+ c->private_data = state;
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = tree->session->transport->socket->event.ctx;
+
+ /* setup structures for opening file */
+ state->io_open = talloc_zero(c, union smb_open);
+ if (state->io_open == NULL) goto failed;
+
+ state->io_open->ntcreatex.level = RAW_OPEN_NTCREATEX;
+ state->io_open->ntcreatex.in.root_fid = 0;
+ state->io_open->ntcreatex.in.flags = 0;
+ state->io_open->ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ state->io_open->ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ state->io_open->ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ state->io_open->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ state->io_open->ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ state->io_open->ntcreatex.in.security_flags = 0;
+ state->io_open->ntcreatex.in.fname = io->in.fname;
+
+ /* send the open on its way */
+ state->req = smb_raw_open_send(tree, state->io_open);
+ if (state->req == NULL) goto failed;
+
+ /* setup the callback handler */
+ state->req->async.fn = appendacl_handler;
+ state->req->async.private_data = c;
+ state->stage = APPENDACL_OPENPATH;
+
+ return c;
+
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+
+/*
+ composite appendacl call - recv side
+*/
+NTSTATUS smb_composite_appendacl_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+ state->io->out.sd = security_descriptor_copy (mem_ctx, state->io->out.sd);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ composite appendacl call - sync interface
+*/
+NTSTATUS smb_composite_appendacl(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_composite_appendacl *io)
+{
+ struct composite_context *c = smb_composite_appendacl_send(tree, io);
+ return smb_composite_appendacl_recv(c, mem_ctx);
+}
+
diff --git a/source4/libcli/smb_composite/connect.c b/source4/libcli/smb_composite/connect.c
new file mode 100644
index 0000000000..3db777ddc8
--- /dev/null
+++ b/source4/libcli/smb_composite/connect.c
@@ -0,0 +1,520 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 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/>.
+*/
+/*
+ a composite API for making a full SMB connection
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "lib/events/events.h"
+#include "libcli/resolve/resolve.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "param/param.h"
+
+/* the stages of this call */
+enum connect_stage {CONNECT_RESOLVE,
+ CONNECT_SOCKET,
+ CONNECT_SESSION_REQUEST,
+ CONNECT_NEGPROT,
+ CONNECT_SESSION_SETUP,
+ CONNECT_SESSION_SETUP_ANON,
+ CONNECT_TCON,
+ CONNECT_DONE
+};
+
+struct connect_state {
+ enum connect_stage stage;
+ struct smbcli_socket *sock;
+ struct smbcli_transport *transport;
+ struct smbcli_session *session;
+ struct smb_composite_connect *io;
+ union smb_tcon *io_tcon;
+ struct smb_composite_sesssetup *io_setup;
+ struct smbcli_request *req;
+ struct composite_context *creq;
+};
+
+
+static void request_handler(struct smbcli_request *);
+static void composite_handler(struct composite_context *);
+
+/*
+ a tree connect request has completed
+*/
+static NTSTATUS connect_tcon(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+
+ status = smb_raw_tcon_recv(state->req, c, state->io_tcon);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ io->out.tree->tid = state->io_tcon->tconx.out.tid;
+ if (state->io_tcon->tconx.out.dev_type) {
+ io->out.tree->device = talloc_strdup(io->out.tree,
+ state->io_tcon->tconx.out.dev_type);
+ }
+ if (state->io_tcon->tconx.out.fs_type) {
+ io->out.tree->fs_type = talloc_strdup(io->out.tree,
+ state->io_tcon->tconx.out.fs_type);
+ }
+
+ state->stage = CONNECT_DONE;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ a session setup request with anonymous fallback has completed
+*/
+static NTSTATUS connect_session_setup_anon(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+
+ status = smb_composite_sesssetup_recv(state->creq);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ io->out.anonymous_fallback_done = true;
+
+ state->session->vuid = state->io_setup->out.vuid;
+
+ /* setup for a tconx */
+ state->io_tcon = talloc(c, union smb_tcon);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_tcon);
+
+ /* connect to a share using a tree connect */
+ state->io_tcon->generic.level = RAW_TCON_TCONX;
+ state->io_tcon->tconx.in.flags = 0;
+ state->io_tcon->tconx.in.password = data_blob(NULL, 0);
+
+ state->io_tcon->tconx.in.path = talloc_asprintf(state->io_tcon,
+ "\\\\%s\\%s",
+ io->in.called_name,
+ io->in.service);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_tcon->tconx.in.path);
+ if (!io->in.service_type) {
+ state->io_tcon->tconx.in.device = "?????";
+ } else {
+ state->io_tcon->tconx.in.device = io->in.service_type;
+ }
+
+ state->req = smb_raw_tcon_send(io->out.tree, state->io_tcon);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+ if (state->req->state == SMBCLI_REQUEST_ERROR) {
+ return state->req->status;
+ }
+
+ state->req->async.fn = request_handler;
+ state->req->async.private_data = c;
+ state->stage = CONNECT_TCON;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ a session setup request has completed
+*/
+static NTSTATUS connect_session_setup(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+
+ status = smb_composite_sesssetup_recv(state->creq);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !cli_credentials_is_anonymous(state->io->in.credentials) &&
+ io->in.fallback_to_anonymous) {
+
+ state->io_setup->in.credentials = cli_credentials_init(state);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_setup->in.credentials);
+ cli_credentials_set_workstation(state->io_setup->in.credentials,
+ cli_credentials_get_workstation(state->io->in.credentials),
+ CRED_SPECIFIED);
+ cli_credentials_set_anonymous(state->io_setup->in.credentials);
+
+ /* If the preceding attempt was with extended security, we
+ * have been given a uid in the NTLMSSP_CHALLENGE reply. This
+ * would lead to an invalid uid in the anonymous fallback */
+ state->session->vuid = 0;
+ data_blob_free(&state->session->user_session_key);
+ talloc_free(state->session->gensec);
+ state->session->gensec = NULL;
+
+ state->creq = smb_composite_sesssetup_send(state->session,
+ state->io_setup);
+ NT_STATUS_HAVE_NO_MEMORY(state->creq);
+ if (state->creq->state == COMPOSITE_STATE_ERROR) {
+ return state->creq->status;
+ }
+ state->creq->async.fn = composite_handler;
+ state->creq->async.private_data = c;
+ state->stage = CONNECT_SESSION_SETUP_ANON;
+
+ return NT_STATUS_OK;
+ }
+
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->session->vuid = state->io_setup->out.vuid;
+
+ /* If we don't have a remote share name then this indicates that
+ * we don't want to do a tree connect */
+ if (!io->in.service) {
+ state->stage = CONNECT_DONE;
+ return NT_STATUS_OK;
+ }
+
+ state->io_tcon = talloc(c, union smb_tcon);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_tcon);
+
+ /* connect to a share using a tree connect */
+ state->io_tcon->generic.level = RAW_TCON_TCONX;
+ state->io_tcon->tconx.in.flags = 0;
+ state->io_tcon->tconx.in.password = data_blob(NULL, 0);
+
+ state->io_tcon->tconx.in.path = talloc_asprintf(state->io_tcon,
+ "\\\\%s\\%s",
+ io->in.called_name,
+ io->in.service);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_tcon->tconx.in.path);
+ if (!io->in.service_type) {
+ state->io_tcon->tconx.in.device = "?????";
+ } else {
+ state->io_tcon->tconx.in.device = io->in.service_type;
+ }
+
+ state->req = smb_raw_tcon_send(io->out.tree, state->io_tcon);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+ if (state->req->state == SMBCLI_REQUEST_ERROR) {
+ return state->req->status;
+ }
+
+ state->req->async.fn = request_handler;
+ state->req->async.private_data = c;
+ state->stage = CONNECT_TCON;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ a negprot request has completed
+*/
+static NTSTATUS connect_negprot(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+
+ status = smb_raw_negotiate_recv(state->req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* next step is a session setup */
+ state->session = smbcli_session_init(state->transport, state, true, io->in.session_options);
+ NT_STATUS_HAVE_NO_MEMORY(state->session);
+
+ /* setup for a tconx (or at least have the structure ready to
+ * return, if we won't go that far) */
+ io->out.tree = smbcli_tree_init(state->session, state, true);
+ NT_STATUS_HAVE_NO_MEMORY(io->out.tree);
+
+ /* If we don't have any credentials then this indicates that
+ * we don't want to do a session setup */
+ if (!io->in.credentials) {
+ state->stage = CONNECT_DONE;
+ return NT_STATUS_OK;
+ }
+
+ state->io_setup = talloc(c, struct smb_composite_sesssetup);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_setup);
+
+ /* prepare a session setup to establish a security context */
+ state->io_setup->in.sesskey = state->transport->negotiate.sesskey;
+ state->io_setup->in.capabilities = state->transport->negotiate.capabilities;
+ state->io_setup->in.credentials = io->in.credentials;
+ state->io_setup->in.workgroup = io->in.workgroup;
+ state->io_setup->in.gensec_settings = io->in.gensec_settings;
+
+ state->creq = smb_composite_sesssetup_send(state->session, state->io_setup);
+ NT_STATUS_HAVE_NO_MEMORY(state->creq);
+ if (state->creq->state == COMPOSITE_STATE_ERROR) {
+ return state->creq->status;
+ }
+
+ state->creq->async.fn = composite_handler;
+ state->creq->async.private_data = c;
+
+ state->stage = CONNECT_SESSION_SETUP;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ setup a negprot send
+*/
+static NTSTATUS connect_send_negprot(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+
+ state->req = smb_raw_negotiate_send(state->transport, io->in.options.unicode, io->in.options.max_protocol);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ state->req->async.fn = request_handler;
+ state->req->async.private_data = c;
+ state->stage = CONNECT_NEGPROT;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ a session request operation has completed
+*/
+static NTSTATUS connect_session_request(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+
+ status = smbcli_transport_connect_recv(state->req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* next step is a negprot */
+ return connect_send_negprot(c, io);
+}
+
+/*
+ a socket connection operation has completed
+*/
+static NTSTATUS connect_socket(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+ struct nbt_name calling, called;
+
+ status = smbcli_sock_connect_recv(state->creq, state, &state->sock);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* the socket is up - we can initialise the smbcli transport layer */
+ state->transport = smbcli_transport_init(state->sock, state, true,
+ &io->in.options, io->in.iconv_convenience);
+ NT_STATUS_HAVE_NO_MEMORY(state->transport);
+
+ if (is_ipaddress(state->sock->hostname) &&
+ (state->io->in.called_name != NULL)) {
+ /* If connecting to an IP address, we might want the real name
+ * of the host for later kerberos. The called name is a better
+ * approximation */
+ state->sock->hostname =
+ talloc_strdup(state->sock, io->in.called_name);
+ NT_STATUS_HAVE_NO_MEMORY(state->sock->hostname);
+ }
+
+ make_nbt_name_client(&calling, cli_credentials_get_workstation(io->in.credentials));
+
+ nbt_choose_called_name(state, &called, io->in.called_name, NBT_NAME_SERVER);
+
+ /* we have a connected socket - next step is a session
+ request, if needed. Port 445 doesn't need it, so it goes
+ straight to the negprot */
+ if (state->sock->port == 445) {
+ status = nbt_name_dup(state->transport, &called,
+ &state->transport->called);
+ NT_STATUS_NOT_OK_RETURN(status);
+ return connect_send_negprot(c, io);
+ }
+
+ state->req = smbcli_transport_connect_send(state->transport, &calling, &called);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ state->req->async.fn = request_handler;
+ state->req->async.private_data = c;
+ state->stage = CONNECT_SESSION_REQUEST;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ called when name resolution is finished
+*/
+static NTSTATUS connect_resolve(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+ const char *address;
+
+ status = resolve_name_recv(state->creq, state, &address);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->creq = smbcli_sock_connect_send(state, address,
+ io->in.dest_ports,
+ io->in.dest_host,
+ NULL, c->event_ctx,
+ io->in.socket_options);
+ NT_STATUS_HAVE_NO_MEMORY(state->creq);
+
+ state->stage = CONNECT_SOCKET;
+ state->creq->async.private_data = c;
+ state->creq->async.fn = composite_handler;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle and dispatch state transitions
+*/
+static void state_handler(struct composite_context *c)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+
+ switch (state->stage) {
+ case CONNECT_RESOLVE:
+ c->status = connect_resolve(c, state->io);
+ break;
+ case CONNECT_SOCKET:
+ c->status = connect_socket(c, state->io);
+ break;
+ case CONNECT_SESSION_REQUEST:
+ c->status = connect_session_request(c, state->io);
+ break;
+ case CONNECT_NEGPROT:
+ c->status = connect_negprot(c, state->io);
+ break;
+ case CONNECT_SESSION_SETUP:
+ c->status = connect_session_setup(c, state->io);
+ break;
+ case CONNECT_SESSION_SETUP_ANON:
+ c->status = connect_session_setup_anon(c, state->io);
+ break;
+ case CONNECT_TCON:
+ c->status = connect_tcon(c, state->io);
+ break;
+ }
+
+ if (state->stage == CONNECT_DONE) {
+ /* all done! */
+ composite_done(c);
+ } else {
+ composite_is_ok(c);
+ }
+}
+
+
+/*
+ handler for completion of a smbcli_request sub-request
+*/
+static void request_handler(struct smbcli_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ state_handler(c);
+}
+
+/*
+ handler for completion of a smbcli_composite sub-request
+*/
+static void composite_handler(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ state_handler(c);
+}
+
+/*
+ a function to establish a smbcli_tree from scratch
+*/
+struct composite_context *smb_composite_connect_send(struct smb_composite_connect *io,
+ TALLOC_CTX *mem_ctx,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *c;
+ struct connect_state *state;
+ struct nbt_name name;
+
+ c = talloc_zero(mem_ctx, struct composite_context);
+ if (c == NULL) goto failed;
+
+ c->event_ctx = talloc_reference(c, event_ctx);
+ if (c->event_ctx == NULL) goto failed;
+
+ state = talloc_zero(c, struct connect_state);
+ if (state == NULL) goto failed;
+
+ if (io->in.gensec_settings == NULL) goto failed;
+ state->io = io;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->private_data = state;
+
+ state->stage = CONNECT_RESOLVE;
+ make_nbt_name_server(&name, io->in.dest_host);
+ state->creq = resolve_name_send(resolve_ctx, &name, c->event_ctx);
+
+ if (state->creq == NULL) goto failed;
+ state->creq->async.private_data = c;
+ state->creq->async.fn = composite_handler;
+
+ return c;
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+/*
+ recv half of async composite connect code
+*/
+NTSTATUS smb_composite_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ talloc_steal(mem_ctx, state->io->out.tree);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+/*
+ sync version of smb_composite_connect
+*/
+NTSTATUS smb_composite_connect(struct smb_composite_connect *io, TALLOC_CTX *mem_ctx,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *ev)
+{
+ struct composite_context *c = smb_composite_connect_send(io, mem_ctx, resolve_ctx, ev);
+ return smb_composite_connect_recv(c, mem_ctx);
+}
diff --git a/source4/libcli/smb_composite/fetchfile.c b/source4/libcli/smb_composite/fetchfile.c
new file mode 100644
index 0000000000..210a2940b9
--- /dev/null
+++ b/source4/libcli/smb_composite/fetchfile.c
@@ -0,0 +1,192 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Volker Lendecke 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/>.
+*/
+/*
+ a composite API for loading a whole file into memory
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/resolve/resolve.h"
+
+enum fetchfile_stage {FETCHFILE_CONNECT,
+ FETCHFILE_READ};
+
+struct fetchfile_state {
+ enum fetchfile_stage stage;
+ struct smb_composite_fetchfile *io;
+ struct composite_context *creq;
+ struct smb_composite_connect *connect;
+ struct smb_composite_loadfile *loadfile;
+};
+
+static void fetchfile_composite_handler(struct composite_context *req);
+
+static NTSTATUS fetchfile_connect(struct composite_context *c,
+ struct smb_composite_fetchfile *io)
+{
+ NTSTATUS status;
+ struct fetchfile_state *state;
+ state = talloc_get_type(c->private_data, struct fetchfile_state);
+
+ status = smb_composite_connect_recv(state->creq, c);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->loadfile = talloc(state, struct smb_composite_loadfile);
+ NT_STATUS_HAVE_NO_MEMORY(state->loadfile);
+
+ state->loadfile->in.fname = io->in.filename;
+
+ state->creq = smb_composite_loadfile_send(state->connect->out.tree,
+ state->loadfile);
+ NT_STATUS_HAVE_NO_MEMORY(state->creq);
+
+ state->creq->async.private_data = c;
+ state->creq->async.fn = fetchfile_composite_handler;
+
+ state->stage = FETCHFILE_READ;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fetchfile_read(struct composite_context *c,
+ struct smb_composite_fetchfile *io)
+{
+ NTSTATUS status;
+ struct fetchfile_state *state;
+ state = talloc_get_type(c->private_data, struct fetchfile_state);
+
+ status = smb_composite_loadfile_recv(state->creq, NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ io->out.data = state->loadfile->out.data;
+ io->out.size = state->loadfile->out.size;
+
+ c->state = COMPOSITE_STATE_DONE;
+ if (c->async.fn)
+ c->async.fn(c);
+
+ return NT_STATUS_OK;
+}
+
+static void fetchfile_state_handler(struct composite_context *c)
+{
+ struct fetchfile_state *state;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ state = talloc_get_type(c->private_data, struct fetchfile_state);
+
+ /* when this handler is called, the stage indicates what
+ call has just finished */
+ switch (state->stage) {
+ case FETCHFILE_CONNECT:
+ status = fetchfile_connect(c, state->io);
+ break;
+ case FETCHFILE_READ:
+ status = fetchfile_read(c, state->io);
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ c->status = status;
+ c->state = COMPOSITE_STATE_ERROR;
+ if (c->async.fn) {
+ c->async.fn(c);
+ }
+ }
+}
+
+static void fetchfile_composite_handler(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ fetchfile_state_handler(c);
+}
+
+struct composite_context *smb_composite_fetchfile_send(struct smb_composite_fetchfile *io,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *c;
+ struct fetchfile_state *state;
+
+ c = talloc_zero(NULL, struct composite_context);
+ if (c == NULL) goto failed;
+
+ state = talloc(c, struct fetchfile_state);
+ if (state == NULL) goto failed;
+
+ state->connect = talloc(state, struct smb_composite_connect);
+ if (state->connect == NULL) goto failed;
+
+ state->io = io;
+
+ state->connect->in.dest_host = io->in.dest_host;
+ state->connect->in.dest_ports = io->in.ports;
+ state->connect->in.socket_options = io->in.socket_options;
+ state->connect->in.called_name = io->in.called_name;
+ state->connect->in.service = io->in.service;
+ state->connect->in.service_type = io->in.service_type;
+ state->connect->in.credentials = io->in.credentials;
+ state->connect->in.fallback_to_anonymous = false;
+ state->connect->in.workgroup = io->in.workgroup;
+ state->connect->in.gensec_settings = io->in.gensec_settings;
+ state->connect->in.iconv_convenience = io->in.iconv_convenience;
+
+ state->connect->in.options = io->in.options;
+ state->connect->in.session_options = io->in.session_options;
+
+ state->creq = smb_composite_connect_send(state->connect, state,
+ io->in.resolve_ctx, event_ctx);
+ if (state->creq == NULL) goto failed;
+
+ state->creq->async.private_data = c;
+ state->creq->async.fn = fetchfile_composite_handler;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ state->stage = FETCHFILE_CONNECT;
+ c->private_data = state;
+
+ return c;
+ failed:
+ talloc_free(c);
+ return NULL;
+}
+
+NTSTATUS smb_composite_fetchfile_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct fetchfile_state *state = talloc_get_type(c->private_data, struct fetchfile_state);
+ talloc_steal(mem_ctx, state->io->out.data);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+NTSTATUS smb_composite_fetchfile(struct smb_composite_fetchfile *io,
+ TALLOC_CTX *mem_ctx)
+{
+ struct composite_context *c = smb_composite_fetchfile_send(io, NULL);
+ return smb_composite_fetchfile_recv(c, mem_ctx);
+}
diff --git a/source4/libcli/smb_composite/fsinfo.c b/source4/libcli/smb_composite/fsinfo.c
new file mode 100644
index 0000000000..3bc93b62ab
--- /dev/null
+++ b/source4/libcli/smb_composite/fsinfo.c
@@ -0,0 +1,210 @@
+/*
+ a composite API for quering file system information
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/resolve/resolve.h"
+
+/* the stages of this call */
+enum fsinfo_stage {FSINFO_CONNECT, FSINFO_QUERY};
+
+
+static void fsinfo_raw_handler(struct smbcli_request *req);
+static void fsinfo_composite_handler(struct composite_context *c);
+static void fsinfo_state_handler(struct composite_context *c);
+
+struct fsinfo_state {
+ enum fsinfo_stage stage;
+ struct composite_context *creq;
+ struct smb_composite_fsinfo *io;
+ struct smb_composite_connect *connect;
+ union smb_fsinfo *fsinfo;
+ struct smbcli_tree *tree;
+ struct smbcli_request *req;
+};
+
+static NTSTATUS fsinfo_connect(struct composite_context *c,
+ struct smb_composite_fsinfo *io)
+{
+ NTSTATUS status;
+ struct fsinfo_state *state;
+ state = talloc_get_type(c->private_data, struct fsinfo_state);
+
+ status = smb_composite_connect_recv(state->creq, c);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->fsinfo = talloc(state, union smb_fsinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->fsinfo);
+
+ state->fsinfo->generic.level = io->in.level;
+
+ state->req = smb_raw_fsinfo_send(state->connect->out.tree,
+ state,
+ state->fsinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ state->req->async.private_data = c;
+ state->req->async.fn = fsinfo_raw_handler;
+
+ state->stage = FSINFO_QUERY;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fsinfo_query(struct composite_context *c,
+ struct smb_composite_fsinfo *io)
+{
+ NTSTATUS status;
+ struct fsinfo_state *state;
+ state = talloc_get_type(c->private_data, struct fsinfo_state);
+
+ status = smb_raw_fsinfo_recv(state->req, state, state->fsinfo);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->io->out.fsinfo = state->fsinfo;
+
+ c->state = COMPOSITE_STATE_DONE;
+
+ if (c->async.fn)
+ c->async.fn(c);
+
+ return NT_STATUS_OK;
+
+}
+
+/*
+ handler for completion of a sub-request in fsinfo
+*/
+static void fsinfo_state_handler(struct composite_context *creq)
+{
+ struct fsinfo_state *state = talloc_get_type(creq->private_data, struct fsinfo_state);
+
+ /* when this handler is called, the stage indicates what
+ call has just finished */
+ switch (state->stage) {
+ case FSINFO_CONNECT:
+ creq->status = fsinfo_connect(creq, state->io);
+ break;
+
+ case FSINFO_QUERY:
+ creq->status = fsinfo_query(creq, state->io);
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(creq->status)) {
+ creq->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (creq->state >= COMPOSITE_STATE_DONE && creq->async.fn) {
+ creq->async.fn(creq);
+ }
+}
+
+/*
+ As raw and composite handlers take different requests, we need to handlers
+ to adapt both for the same state machine in fsinfo_state_handler()
+*/
+static void fsinfo_raw_handler(struct smbcli_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ fsinfo_state_handler(c);
+}
+
+static void fsinfo_composite_handler(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ fsinfo_state_handler(c);
+}
+
+/*
+ composite fsinfo call - connects to a tree and queries a file system information
+*/
+struct composite_context *smb_composite_fsinfo_send(struct smbcli_tree *tree,
+ struct smb_composite_fsinfo *io,
+ struct resolve_context *resolve_ctx)
+{
+ struct composite_context *c;
+ struct fsinfo_state *state;
+
+ c = talloc_zero(tree, struct composite_context);
+ if (c == NULL) goto failed;
+
+ state = talloc(c, struct fsinfo_state);
+ if (state == NULL) goto failed;
+
+ state->io = io;
+
+ state->connect = talloc(state, struct smb_composite_connect);
+
+ if (state->connect == NULL) goto failed;
+
+ state->connect->in.dest_host = io->in.dest_host;
+ state->connect->in.dest_ports = io->in.dest_ports;
+ state->connect->in.socket_options = io->in.socket_options;
+ state->connect->in.called_name = io->in.called_name;
+ state->connect->in.service = io->in.service;
+ state->connect->in.service_type = io->in.service_type;
+ state->connect->in.credentials = io->in.credentials;
+ state->connect->in.fallback_to_anonymous = false;
+ state->connect->in.workgroup = io->in.workgroup;
+ state->connect->in.iconv_convenience = io->in.iconv_convenience;
+ state->connect->in.gensec_settings = io->in.gensec_settings;
+
+ state->connect->in.options = tree->session->transport->options;
+ state->connect->in.session_options = tree->session->options;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ state->stage = FSINFO_CONNECT;
+ c->private_data = state;
+
+ state->creq = smb_composite_connect_send(state->connect, state,
+ resolve_ctx, c->event_ctx);
+
+ if (state->creq == NULL) goto failed;
+
+ state->creq->async.private_data = c;
+ state->creq->async.fn = fsinfo_composite_handler;
+
+ return c;
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+/*
+ composite fsinfo call - recv side
+*/
+NTSTATUS smb_composite_fsinfo_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct fsinfo_state *state = talloc_get_type(c->private_data, struct fsinfo_state);
+ talloc_steal(mem_ctx, state->io->out.fsinfo);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ composite fsinfo call - sync interface
+*/
+NTSTATUS smb_composite_fsinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_composite_fsinfo *io,
+ struct resolve_context *resolve_ctx)
+{
+ struct composite_context *c = smb_composite_fsinfo_send(tree, io, resolve_ctx);
+ return smb_composite_fsinfo_recv(c, mem_ctx);
+}
+
diff --git a/source4/libcli/smb_composite/loadfile.c b/source4/libcli/smb_composite/loadfile.c
new file mode 100644
index 0000000000..994c29c77d
--- /dev/null
+++ b/source4/libcli/smb_composite/loadfile.c
@@ -0,0 +1,293 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 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/>.
+*/
+/*
+ a composite API for loading a whole file into memory
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/* the stages of this call */
+enum loadfile_stage {LOADFILE_OPEN, LOADFILE_READ, LOADFILE_CLOSE};
+
+
+static void loadfile_handler(struct smbcli_request *req);
+
+struct loadfile_state {
+ enum loadfile_stage stage;
+ struct smb_composite_loadfile *io;
+ struct smbcli_request *req;
+ union smb_open *io_open;
+ union smb_read *io_read;
+};
+
+/*
+ setup for the close
+*/
+static NTSTATUS setup_close(struct composite_context *c,
+ struct smbcli_tree *tree, uint16_t fnum)
+{
+ struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state);
+ union smb_close *io_close;
+
+ /* nothing to read, setup the close */
+ io_close = talloc(c, union smb_close);
+ NT_STATUS_HAVE_NO_MEMORY(io_close);
+
+ io_close->close.level = RAW_CLOSE_CLOSE;
+ io_close->close.in.file.fnum = fnum;
+ io_close->close.in.write_time = 0;
+
+ state->req = smb_raw_close_send(tree, io_close);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler again when the close is done */
+ state->req->async.fn = loadfile_handler;
+ state->req->async.private_data = c;
+ state->stage = LOADFILE_CLOSE;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called when the open is done - pull the results and setup for the
+ first readx, or close if the file is zero size
+*/
+static NTSTATUS loadfile_open(struct composite_context *c,
+ struct smb_composite_loadfile *io)
+{
+ struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state);
+ struct smbcli_tree *tree = state->req->tree;
+ NTSTATUS status;
+
+ status = smb_raw_open_recv(state->req, c, state->io_open);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* don't allow stupidly large loads */
+ if (state->io_open->ntcreatex.out.size > 100*1000*1000) {
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* allocate space for the file data */
+ io->out.size = state->io_open->ntcreatex.out.size;
+ io->out.data = talloc_array(c, uint8_t, io->out.size);
+ NT_STATUS_HAVE_NO_MEMORY(io->out.data);
+
+ if (io->out.size == 0) {
+ return setup_close(c, tree, state->io_open->ntcreatex.out.file.fnum);
+ }
+
+ /* setup for the read */
+ state->io_read = talloc(c, union smb_read);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_read);
+
+ state->io_read->readx.level = RAW_READ_READX;
+ state->io_read->readx.in.file.fnum = state->io_open->ntcreatex.out.file.fnum;
+ state->io_read->readx.in.offset = 0;
+ state->io_read->readx.in.mincnt = MIN(32768, io->out.size);
+ state->io_read->readx.in.maxcnt = state->io_read->readx.in.mincnt;
+ state->io_read->readx.in.remaining = 0;
+ state->io_read->readx.in.read_for_execute = false;
+ state->io_read->readx.out.data = io->out.data;
+
+ state->req = smb_raw_read_send(tree, state->io_read);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler again when the first read is done */
+ state->req->async.fn = loadfile_handler;
+ state->req->async.private_data = c;
+ state->stage = LOADFILE_READ;
+
+ talloc_free(state->io_open);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ called when a read is done - pull the results and setup for the
+ next read, or close if the file is all done
+*/
+static NTSTATUS loadfile_read(struct composite_context *c,
+ struct smb_composite_loadfile *io)
+{
+ struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state);
+ struct smbcli_tree *tree = state->req->tree;
+ NTSTATUS status;
+
+ status = smb_raw_read_recv(state->req, state->io_read);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* we might be done */
+ if (state->io_read->readx.in.offset +
+ state->io_read->readx.out.nread == io->out.size) {
+ return setup_close(c, tree, state->io_read->readx.in.file.fnum);
+ }
+
+ /* setup for the next read */
+ state->io_read->readx.in.offset += state->io_read->readx.out.nread;
+ state->io_read->readx.in.mincnt = MIN(32768, io->out.size - state->io_read->readx.in.offset);
+ state->io_read->readx.out.data = io->out.data + state->io_read->readx.in.offset;
+
+ state->req = smb_raw_read_send(tree, state->io_read);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler again when the read is done */
+ state->req->async.fn = loadfile_handler;
+ state->req->async.private_data = c;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called when the close is done, check the status and cleanup
+*/
+static NTSTATUS loadfile_close(struct composite_context *c,
+ struct smb_composite_loadfile *io)
+{
+ struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state);
+ NTSTATUS status;
+
+ status = smbcli_request_simple_recv(state->req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ c->state = COMPOSITE_STATE_DONE;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handler for completion of a sub-request in loadfile
+*/
+static void loadfile_handler(struct smbcli_request *req)
+{
+ struct composite_context *c = (struct composite_context *)req->async.private_data;
+ struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state);
+
+ /* when this handler is called, the stage indicates what
+ call has just finished */
+ switch (state->stage) {
+ case LOADFILE_OPEN:
+ c->status = loadfile_open(c, state->io);
+ break;
+
+ case LOADFILE_READ:
+ c->status = loadfile_read(c, state->io);
+ break;
+
+ case LOADFILE_CLOSE:
+ c->status = loadfile_close(c, state->io);
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (c->state >= COMPOSITE_STATE_DONE &&
+ c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+/*
+ composite loadfile call - does an openx followed by a number of readx calls,
+ followed by a close
+*/
+struct composite_context *smb_composite_loadfile_send(struct smbcli_tree *tree,
+ struct smb_composite_loadfile *io)
+{
+ struct composite_context *c;
+ struct loadfile_state *state;
+
+ c = talloc_zero(tree, struct composite_context);
+ if (c == NULL) goto failed;
+
+ state = talloc(c, struct loadfile_state);
+ if (state == NULL) goto failed;
+
+ state->io = io;
+
+ c->private_data = state;
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = tree->session->transport->socket->event.ctx;
+
+ /* setup for the open */
+ state->io_open = talloc_zero(c, union smb_open);
+ if (state->io_open == NULL) goto failed;
+
+ state->io_open->ntcreatex.level = RAW_OPEN_NTCREATEX;
+ state->io_open->ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ state->io_open->ntcreatex.in.access_mask = SEC_FILE_READ_DATA;
+ state->io_open->ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ state->io_open->ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ state->io_open->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ state->io_open->ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ state->io_open->ntcreatex.in.fname = io->in.fname;
+
+ /* send the open on its way */
+ state->req = smb_raw_open_send(tree, state->io_open);
+ if (state->req == NULL) goto failed;
+
+ /* setup the callback handler */
+ state->req->async.fn = loadfile_handler;
+ state->req->async.private_data = c;
+ state->stage = LOADFILE_OPEN;
+
+ return c;
+
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+
+/*
+ composite loadfile call - recv side
+*/
+NTSTATUS smb_composite_loadfile_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state);
+ talloc_steal(mem_ctx, state->io->out.data);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ composite loadfile call - sync interface
+*/
+NTSTATUS smb_composite_loadfile(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_composite_loadfile *io)
+{
+ struct composite_context *c = smb_composite_loadfile_send(tree, io);
+ return smb_composite_loadfile_recv(c, mem_ctx);
+}
+
diff --git a/source4/libcli/smb_composite/savefile.c b/source4/libcli/smb_composite/savefile.c
new file mode 100644
index 0000000000..25a35c01a9
--- /dev/null
+++ b/source4/libcli/smb_composite/savefile.c
@@ -0,0 +1,288 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 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/>.
+*/
+/*
+ a composite API for saving a whole file from memory
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/* the stages of this call */
+enum savefile_stage {SAVEFILE_OPEN, SAVEFILE_WRITE, SAVEFILE_CLOSE};
+
+static void savefile_handler(struct smbcli_request *req);
+
+struct savefile_state {
+ enum savefile_stage stage;
+ off_t total_written;
+ struct smb_composite_savefile *io;
+ union smb_open *io_open;
+ union smb_write *io_write;
+ struct smbcli_request *req;
+};
+
+
+/*
+ setup for the close
+*/
+static NTSTATUS setup_close(struct composite_context *c,
+ struct smbcli_tree *tree, uint16_t fnum)
+{
+ struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state);
+ union smb_close *io_close;
+
+ /* nothing to write, setup the close */
+ io_close = talloc(c, union smb_close);
+ NT_STATUS_HAVE_NO_MEMORY(io_close);
+
+ io_close->close.level = RAW_CLOSE_CLOSE;
+ io_close->close.in.file.fnum = fnum;
+ io_close->close.in.write_time = 0;
+
+ state->req = smb_raw_close_send(tree, io_close);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler again when the close is done */
+ state->stage = SAVEFILE_CLOSE;
+ state->req->async.fn = savefile_handler;
+ state->req->async.private_data = c;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called when the open is done - pull the results and setup for the
+ first writex, or close if the file is zero size
+*/
+static NTSTATUS savefile_open(struct composite_context *c,
+ struct smb_composite_savefile *io)
+{
+ struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state);
+ union smb_write *io_write;
+ struct smbcli_tree *tree = state->req->tree;
+ NTSTATUS status;
+ uint32_t max_xmit = tree->session->transport->negotiate.max_xmit;
+
+ status = smb_raw_open_recv(state->req, c, state->io_open);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (io->in.size == 0) {
+ return setup_close(c, tree, state->io_open->ntcreatex.out.file.fnum);
+ }
+
+ /* setup for the first write */
+ io_write = talloc(c, union smb_write);
+ NT_STATUS_HAVE_NO_MEMORY(io_write);
+
+ io_write->writex.level = RAW_WRITE_WRITEX;
+ io_write->writex.in.file.fnum = state->io_open->ntcreatex.out.file.fnum;
+ io_write->writex.in.offset = 0;
+ io_write->writex.in.wmode = 0;
+ io_write->writex.in.remaining = 0;
+ io_write->writex.in.count = MIN(max_xmit - 100, io->in.size);
+ io_write->writex.in.data = io->in.data;
+ state->io_write = io_write;
+
+ state->req = smb_raw_write_send(tree, io_write);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler again when the first write is done */
+ state->stage = SAVEFILE_WRITE;
+ state->req->async.fn = savefile_handler;
+ state->req->async.private_data = c;
+ talloc_free(state->io_open);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ called when a write is done - pull the results and setup for the
+ next write, or close if the file is all done
+*/
+static NTSTATUS savefile_write(struct composite_context *c,
+ struct smb_composite_savefile *io)
+{
+ struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state);
+ struct smbcli_tree *tree = state->req->tree;
+ NTSTATUS status;
+ uint32_t max_xmit = tree->session->transport->negotiate.max_xmit;
+
+ status = smb_raw_write_recv(state->req, state->io_write);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->total_written += state->io_write->writex.out.nwritten;
+
+ /* we might be done */
+ if (state->io_write->writex.out.nwritten != state->io_write->writex.in.count ||
+ state->total_written == io->in.size) {
+ return setup_close(c, tree, state->io_write->writex.in.file.fnum);
+ }
+
+ /* setup for the next write */
+ state->io_write->writex.in.offset = state->total_written;
+ state->io_write->writex.in.count = MIN(max_xmit - 100,
+ io->in.size - state->total_written);
+ state->io_write->writex.in.data = io->in.data + state->total_written;
+
+ state->req = smb_raw_write_send(tree, state->io_write);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler again when the write is done */
+ state->req->async.fn = savefile_handler;
+ state->req->async.private_data = c;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called when the close is done, check the status and cleanup
+*/
+static NTSTATUS savefile_close(struct composite_context *c,
+ struct smb_composite_savefile *io)
+{
+ struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state);
+ NTSTATUS status;
+
+ status = smbcli_request_simple_recv(state->req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (state->total_written != io->in.size) {
+ return NT_STATUS_DISK_FULL;
+ }
+
+ c->state = COMPOSITE_STATE_DONE;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handler for completion of a sub-request in savefile
+*/
+static void savefile_handler(struct smbcli_request *req)
+{
+ struct composite_context *c = (struct composite_context *)req->async.private_data;
+ struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state);
+
+ /* when this handler is called, the stage indicates what
+ call has just finished */
+ switch (state->stage) {
+ case SAVEFILE_OPEN:
+ c->status = savefile_open(c, state->io);
+ break;
+
+ case SAVEFILE_WRITE:
+ c->status = savefile_write(c, state->io);
+ break;
+
+ case SAVEFILE_CLOSE:
+ c->status = savefile_close(c, state->io);
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (c->state >= COMPOSITE_STATE_DONE &&
+ c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+/*
+ composite savefile call - does an openx followed by a number of writex calls,
+ followed by a close
+*/
+struct composite_context *smb_composite_savefile_send(struct smbcli_tree *tree,
+ struct smb_composite_savefile *io)
+{
+ struct composite_context *c;
+ struct savefile_state *state;
+ union smb_open *io_open;
+
+ c = talloc_zero(tree, struct composite_context);
+ if (c == NULL) goto failed;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = tree->session->transport->socket->event.ctx;
+
+ state = talloc(c, struct savefile_state);
+ if (state == NULL) goto failed;
+
+ state->stage = SAVEFILE_OPEN;
+ state->total_written = 0;
+ state->io = io;
+
+ /* setup for the open */
+ io_open = talloc_zero(c, union smb_open);
+ if (io_open == NULL) goto failed;
+
+ io_open->ntcreatex.level = RAW_OPEN_NTCREATEX;
+ io_open->ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io_open->ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ io_open->ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io_open->ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io_open->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io_open->ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io_open->ntcreatex.in.fname = io->in.fname;
+ state->io_open = io_open;
+
+ /* send the open on its way */
+ state->req = smb_raw_open_send(tree, io_open);
+ if (state->req == NULL) goto failed;
+
+ /* setup the callback handler */
+ state->req->async.fn = savefile_handler;
+ state->req->async.private_data = c;
+ c->private_data = state;
+
+ return c;
+
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+
+/*
+ composite savefile call - recv side
+*/
+NTSTATUS smb_composite_savefile_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+ status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ composite savefile call - sync interface
+*/
+NTSTATUS smb_composite_savefile(struct smbcli_tree *tree,
+ struct smb_composite_savefile *io)
+{
+ struct composite_context *c = smb_composite_savefile_send(tree, io);
+ return smb_composite_savefile_recv(c);
+}
diff --git a/source4/libcli/smb_composite/sesssetup.c b/source4/libcli/smb_composite/sesssetup.c
new file mode 100644
index 0000000000..83d15e98eb
--- /dev/null
+++ b/source4/libcli/smb_composite/sesssetup.c
@@ -0,0 +1,570 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 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/>.
+*/
+/*
+ a composite API for making handling a generic async session setup
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/smb_composite/proto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "version.h"
+#include "param/param.h"
+
+struct sesssetup_state {
+ union smb_sesssetup setup;
+ NTSTATUS remote_status;
+ NTSTATUS gensec_status;
+ struct smb_composite_sesssetup *io;
+ struct smbcli_request *req;
+};
+
+static int sesssetup_state_destructor(struct sesssetup_state *state)
+{
+ if (state->req) {
+ talloc_free(state->req);
+ state->req = NULL;
+ }
+
+ return 0;
+}
+
+static NTSTATUS session_setup_old(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req);
+static NTSTATUS session_setup_nt1(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req);
+static NTSTATUS session_setup_spnego(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req);
+
+/*
+ store the user session key for a transport
+*/
+static void set_user_session_key(struct smbcli_session *session,
+ const DATA_BLOB *session_key)
+{
+ session->user_session_key = data_blob_talloc(session,
+ session_key->data,
+ session_key->length);
+}
+
+/*
+ handler for completion of a smbcli_request sub-request
+*/
+static void request_handler(struct smbcli_request *req)
+{
+ struct composite_context *c = (struct composite_context *)req->async.private_data;
+ struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
+ struct smbcli_session *session = req->session;
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ DATA_BLOB null_data_blob = data_blob(NULL, 0);
+ NTSTATUS session_key_err, nt_status;
+ struct smbcli_request *check_req = NULL;
+
+ if (req->sign_caller_checks) {
+ req->do_not_free = true;
+ check_req = req;
+ }
+
+ state->remote_status = smb_raw_sesssetup_recv(req, state, &state->setup);
+ c->status = state->remote_status;
+ state->req = NULL;
+
+ /*
+ * we only need to check the signature if the
+ * NT_STATUS_OK is returned
+ */
+ if (!NT_STATUS_IS_OK(state->remote_status)) {
+ talloc_free(check_req);
+ check_req = NULL;
+ }
+
+ switch (state->setup.old.level) {
+ case RAW_SESSSETUP_OLD:
+ state->io->out.vuid = state->setup.old.out.vuid;
+ /* This doesn't work, as this only happens on old
+ * protocols, where this comparison won't match. */
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
+ /* we neet to reset the vuid for a new try */
+ session->vuid = 0;
+ if (cli_credentials_wrong_password(state->io->in.credentials)) {
+ nt_status = session_setup_old(c, session,
+ state->io,
+ &state->req);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(check_req);
+ c->status = nt_status;
+ composite_continue_smb(c, state->req, request_handler, c);
+ return;
+ }
+ }
+ }
+ break;
+
+ case RAW_SESSSETUP_NT1:
+ state->io->out.vuid = state->setup.nt1.out.vuid;
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
+ /* we neet to reset the vuid for a new try */
+ session->vuid = 0;
+ if (cli_credentials_wrong_password(state->io->in.credentials)) {
+ nt_status = session_setup_nt1(c, session,
+ state->io,
+ &state->req);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(check_req);
+ c->status = nt_status;
+ composite_continue_smb(c, state->req, request_handler, c);
+ return;
+ }
+ }
+ }
+ break;
+
+ case RAW_SESSSETUP_SPNEGO:
+ state->io->out.vuid = state->setup.spnego.out.vuid;
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
+ /* we need to reset the vuid for a new try */
+ session->vuid = 0;
+ if (cli_credentials_wrong_password(state->io->in.credentials)) {
+ nt_status = session_setup_spnego(c, session,
+ state->io,
+ &state->req);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(check_req);
+ c->status = nt_status;
+ composite_continue_smb(c, state->req, request_handler, c);
+ return;
+ }
+ }
+ }
+ if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(c->status)) {
+ break;
+ }
+ if (NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+
+ /* The status value here, from the earlier pass at GENSEC is
+ * vital to the security of the system. Even if the other end
+ * accepts, if GENSEC claims 'MORE_PROCESSING_REQUIRED' then
+ * you must keep feeding it blobs, or else the remote
+ * host/attacker might avoid mutal authentication
+ * requirements */
+
+ state->gensec_status = gensec_update(session->gensec, state,
+ state->setup.spnego.out.secblob,
+ &state->setup.spnego.in.secblob);
+ c->status = state->gensec_status;
+ if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(c->status)) {
+ break;
+ }
+ } else {
+ state->setup.spnego.in.secblob = data_blob(NULL, 0);
+ }
+
+ if (NT_STATUS_IS_OK(state->remote_status)) {
+ if (state->setup.spnego.in.secblob.length) {
+ c->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+ session_key_err = gensec_session_key(session->gensec, &session_key);
+ if (NT_STATUS_IS_OK(session_key_err)) {
+ set_user_session_key(session, &session_key);
+ smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob);
+ }
+ }
+
+ if (state->setup.spnego.in.secblob.length) {
+ /*
+ * set the session->vuid value only for calling
+ * smb_raw_sesssetup_send()
+ */
+ uint16_t vuid = session->vuid;
+ session->vuid = state->io->out.vuid;
+ state->req = smb_raw_sesssetup_send(session, &state->setup);
+ session->vuid = vuid;
+ if (state->req) {
+ state->req->sign_caller_checks = true;
+ }
+ composite_continue_smb(c, state->req, request_handler, c);
+ return;
+ }
+ break;
+
+ case RAW_SESSSETUP_SMB2:
+ c->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+ if (check_req) {
+ check_req->sign_caller_checks = false;
+ if (!smbcli_request_check_sign_mac(check_req)) {
+ c->status = NT_STATUS_ACCESS_DENIED;
+ }
+ talloc_free(check_req);
+ check_req = NULL;
+ }
+
+ /* enforce the local signing required flag */
+ if (NT_STATUS_IS_OK(c->status) && !cli_credentials_is_anonymous(state->io->in.credentials)) {
+ if (!session->transport->negotiate.sign_info.doing_signing
+ && session->transport->negotiate.sign_info.mandatory_signing) {
+ DEBUG(0, ("SMB signing required, but server does not support it\n"));
+ c->status = NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ composite_done(c);
+}
+
+
+/*
+ send a nt1 style session setup
+*/
+static NTSTATUS session_setup_nt1(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req)
+{
+ NTSTATUS nt_status = NT_STATUS_INTERNAL_ERROR;
+ struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(state, session->transport->socket->hostname, cli_credentials_get_domain(io->in.credentials));
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ int flags = CLI_CRED_NTLM_AUTH;
+
+ smbcli_temp_set_signing(session->transport);
+
+ if (session->options.lanman_auth) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (session->options.ntlmv2_auth) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ state->setup.nt1.level = RAW_SESSSETUP_NT1;
+ state->setup.nt1.in.bufsize = session->transport->options.max_xmit;
+ state->setup.nt1.in.mpx_max = session->transport->options.max_mux;
+ state->setup.nt1.in.vc_num = 1;
+ state->setup.nt1.in.sesskey = io->in.sesskey;
+ state->setup.nt1.in.capabilities = io->in.capabilities;
+ state->setup.nt1.in.os = "Unix";
+ state->setup.nt1.in.lanman = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING);
+
+ cli_credentials_get_ntlm_username_domain(io->in.credentials, state,
+ &state->setup.nt1.in.user,
+ &state->setup.nt1.in.domain);
+
+
+ if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
+ nt_status = cli_credentials_get_ntlm_response(io->in.credentials, state,
+ &flags,
+ session->transport->negotiate.secblob,
+ names_blob,
+ &state->setup.nt1.in.password1,
+ &state->setup.nt1.in.password2,
+ NULL, &session_key);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+ } else if (session->options.plaintext_auth) {
+ const char *password = cli_credentials_get_password(io->in.credentials);
+ state->setup.nt1.in.password1 = data_blob_talloc(state, password, strlen(password));
+ state->setup.nt1.in.password2 = data_blob(NULL, 0);
+ } else {
+ /* could match windows client and return 'cannot logon from this workstation', but it just confuses everybody */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *req = smb_raw_sesssetup_send(session, &state->setup);
+ if (!*req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ smbcli_transport_simple_set_signing(session->transport, session_key,
+ state->setup.nt1.in.password2);
+ set_user_session_key(session, &session_key);
+
+ data_blob_free(&session_key);
+ }
+
+ return (*req)->status;
+}
+
+
+/*
+ old style session setup (pre NT1 protocol level)
+*/
+static NTSTATUS session_setup_old(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req)
+{
+ NTSTATUS nt_status;
+ struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
+ const char *password = cli_credentials_get_password(io->in.credentials);
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(state, session->transport->socket->hostname, cli_credentials_get_domain(io->in.credentials));
+ DATA_BLOB session_key;
+ int flags = 0;
+ if (session->options.lanman_auth) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (session->options.ntlmv2_auth) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ state->setup.old.level = RAW_SESSSETUP_OLD;
+ state->setup.old.in.bufsize = session->transport->options.max_xmit;
+ state->setup.old.in.mpx_max = session->transport->options.max_mux;
+ state->setup.old.in.vc_num = 1;
+ state->setup.old.in.sesskey = io->in.sesskey;
+ state->setup.old.in.os = "Unix";
+ state->setup.old.in.lanman = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING);
+ cli_credentials_get_ntlm_username_domain(io->in.credentials, state,
+ &state->setup.old.in.user,
+ &state->setup.old.in.domain);
+
+ if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
+ nt_status = cli_credentials_get_ntlm_response(io->in.credentials, state,
+ &flags,
+ session->transport->negotiate.secblob,
+ names_blob,
+ &state->setup.old.in.password,
+ NULL,
+ NULL, &session_key);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+ set_user_session_key(session, &session_key);
+
+ data_blob_free(&session_key);
+ } else if (session->options.plaintext_auth) {
+ state->setup.old.in.password = data_blob_talloc(state, password, strlen(password));
+ } else {
+ /* could match windows client and return 'cannot logon from this workstation', but it just confuses everybody */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *req = smb_raw_sesssetup_send(session, &state->setup);
+ if (!*req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return (*req)->status;
+}
+
+
+/*
+ Modern, all singing, all dancing extended security (and possibly SPNEGO) request
+*/
+static NTSTATUS session_setup_spnego(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req)
+{
+ struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
+ NTSTATUS status;
+ const char *chosen_oid = NULL;
+
+ state->setup.spnego.level = RAW_SESSSETUP_SPNEGO;
+ state->setup.spnego.in.bufsize = session->transport->options.max_xmit;
+ state->setup.spnego.in.mpx_max = session->transport->options.max_mux;
+ state->setup.spnego.in.vc_num = 1;
+ state->setup.spnego.in.sesskey = io->in.sesskey;
+ state->setup.spnego.in.capabilities = io->in.capabilities;
+ state->setup.spnego.in.os = "Unix";
+ state->setup.spnego.in.lanman = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING);
+ state->setup.spnego.in.workgroup = io->in.workgroup;
+
+ smbcli_temp_set_signing(session->transport);
+
+ status = gensec_client_start(session, &session->gensec, c->event_ctx,
+ io->in.gensec_settings);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
+
+ status = gensec_set_credentials(session->gensec, io->in.credentials);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set GENSEC client credentials: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ status = gensec_set_target_hostname(session->gensec, session->transport->socket->hostname);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ status = gensec_set_target_service(session->gensec, "cifs");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set GENSEC target service: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (session->transport->negotiate.secblob.length) {
+ chosen_oid = GENSEC_OID_SPNEGO;
+ status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
+ gensec_get_name_by_oid(session->gensec, chosen_oid), nt_errstr(status)));
+ chosen_oid = GENSEC_OID_NTLMSSP;
+ status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set (fallback) GENSEC client mechanism %s: %s\n",
+ gensec_get_name_by_oid(session->gensec, chosen_oid),
+ nt_errstr(status)));
+ return status;
+ }
+ }
+ } else {
+ /* without a sec blob, means raw NTLMSSP */
+ chosen_oid = GENSEC_OID_NTLMSSP;
+ status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
+ gensec_get_name_by_oid(session->gensec, chosen_oid), nt_errstr(status)));
+ }
+ }
+
+ if ((const void *)chosen_oid == (const void *)GENSEC_OID_SPNEGO) {
+ status = gensec_update(session->gensec, state,
+ session->transport->negotiate.secblob,
+ &state->setup.spnego.in.secblob);
+ } else {
+ status = gensec_update(session->gensec, state,
+ data_blob(NULL, 0),
+ &state->setup.spnego.in.secblob);
+
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed initial gensec_update with mechanism %s: %s\n",
+ gensec_get_name_by_oid(session->gensec, chosen_oid),
+ nt_errstr(status)));
+ return status;
+ }
+ state->gensec_status = status;
+
+ *req = smb_raw_sesssetup_send(session, &state->setup);
+ if (!*req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * we need to check the signature ourself
+ * as the session key might be the acceptor subkey
+ * which comes within the response itself
+ */
+ (*req)->sign_caller_checks = true;
+
+ return (*req)->status;
+}
+
+
+/*
+ composite session setup function that hides the details of all the
+ different session setup varients, including the multi-pass nature of
+ the spnego varient
+*/
+struct composite_context *smb_composite_sesssetup_send(struct smbcli_session *session,
+ struct smb_composite_sesssetup *io)
+{
+ struct composite_context *c;
+ struct sesssetup_state *state;
+ NTSTATUS status;
+
+ c = composite_create(session, session->transport->socket->event.ctx);
+ if (c == NULL) return NULL;
+
+ state = talloc_zero(c, struct sesssetup_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->io = io;
+
+ talloc_set_destructor(state, sesssetup_state_destructor);
+
+ /* no session setup at all in earliest protocol varients */
+ if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
+ ZERO_STRUCT(io->out);
+ composite_done(c);
+ return c;
+ }
+
+ /* see what session setup interface we will use */
+ if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
+ status = session_setup_old(c, session, io, &state->req);
+ } else if (!session->transport->options.use_spnego ||
+ !(io->in.capabilities & CAP_EXTENDED_SECURITY)) {
+ status = session_setup_nt1(c, session, io, &state->req);
+ } else {
+ status = session_setup_spnego(c, session, io, &state->req);
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
+ NT_STATUS_IS_OK(status)) {
+ composite_continue_smb(c, state->req, request_handler, c);
+ return c;
+ }
+
+ composite_error(c, status);
+ return c;
+}
+
+
+/*
+ receive a composite session setup reply
+*/
+NTSTATUS smb_composite_sesssetup_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+ status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+/*
+ sync version of smb_composite_sesssetup
+*/
+NTSTATUS smb_composite_sesssetup(struct smbcli_session *session, struct smb_composite_sesssetup *io)
+{
+ struct composite_context *c = smb_composite_sesssetup_send(session, io);
+ return smb_composite_sesssetup_recv(c);
+}
diff --git a/source4/libcli/smb_composite/smb2.c b/source4/libcli/smb_composite/smb2.c
new file mode 100644
index 0000000000..d71708a974
--- /dev/null
+++ b/source4/libcli/smb_composite/smb2.c
@@ -0,0 +1,370 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 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/>.
+*/
+/*
+ a composite API for making SMB-like calls using SMB2. This is useful
+ as SMB2 often requires more than one requests where a single SMB
+ request would do. In converting code that uses SMB to use SMB2,
+ these routines make life a lot easier
+*/
+
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ continue after a SMB2 close
+ */
+static void continue_close(struct smb2_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ NTSTATUS status;
+ struct smb2_close close_parm;
+
+ status = smb2_close_recv(req, &close_parm);
+ composite_error(ctx, status);
+}
+
+/*
+ continue after the create in a composite unlink
+ */
+static void continue_unlink(struct smb2_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_tree *tree = req->tree;
+ struct smb2_create create_parm;
+ struct smb2_close close_parm;
+ NTSTATUS status;
+
+ status = smb2_create_recv(req, ctx, &create_parm);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(ctx, status);
+ return;
+ }
+
+ ZERO_STRUCT(close_parm);
+ close_parm.in.file.handle = create_parm.out.file.handle;
+
+ req = smb2_close_send(tree, &close_parm);
+ composite_continue_smb2(ctx, req, continue_close, ctx);
+}
+
+/*
+ composite SMB2 unlink call
+*/
+struct composite_context *smb2_composite_unlink_send(struct smb2_tree *tree,
+ union smb_unlink *io)
+{
+ struct composite_context *ctx;
+ struct smb2_create create_parm;
+ struct smb2_request *req;
+
+ ctx = composite_create(tree, tree->session->transport->socket->event.ctx);
+ if (ctx == NULL) return NULL;
+
+ /* check for wildcards - we could support these with a
+ search, but for now they aren't necessary */
+ if (strpbrk(io->unlink.in.pattern, "*?<>") != NULL) {
+ composite_error(ctx, NT_STATUS_NOT_SUPPORTED);
+ return ctx;
+ }
+
+ ZERO_STRUCT(create_parm);
+ create_parm.in.desired_access = SEC_STD_DELETE;
+ create_parm.in.create_disposition = NTCREATEX_DISP_OPEN;
+ create_parm.in.share_access =
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ create_parm.in.create_options =
+ NTCREATEX_OPTIONS_DELETE_ON_CLOSE |
+ NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
+ create_parm.in.fname = io->unlink.in.pattern;
+ if (create_parm.in.fname[0] == '\\') {
+ create_parm.in.fname++;
+ }
+
+ req = smb2_create_send(tree, &create_parm);
+
+ composite_continue_smb2(ctx, req, continue_unlink, ctx);
+ return ctx;
+}
+
+
+/*
+ composite unlink call - sync interface
+*/
+NTSTATUS smb2_composite_unlink(struct smb2_tree *tree, union smb_unlink *io)
+{
+ struct composite_context *c = smb2_composite_unlink_send(tree, io);
+ return composite_wait_free(c);
+}
+
+
+
+
+/*
+ continue after the create in a composite mkdir
+ */
+static void continue_mkdir(struct smb2_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_tree *tree = req->tree;
+ struct smb2_create create_parm;
+ struct smb2_close close_parm;
+ NTSTATUS status;
+
+ status = smb2_create_recv(req, ctx, &create_parm);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(ctx, status);
+ return;
+ }
+
+ ZERO_STRUCT(close_parm);
+ close_parm.in.file.handle = create_parm.out.file.handle;
+
+ req = smb2_close_send(tree, &close_parm);
+ composite_continue_smb2(ctx, req, continue_close, ctx);
+}
+
+/*
+ composite SMB2 mkdir call
+*/
+struct composite_context *smb2_composite_mkdir_send(struct smb2_tree *tree,
+ union smb_mkdir *io)
+{
+ struct composite_context *ctx;
+ struct smb2_create create_parm;
+ struct smb2_request *req;
+
+ ctx = composite_create(tree, tree->session->transport->socket->event.ctx);
+ if (ctx == NULL) return NULL;
+
+ ZERO_STRUCT(create_parm);
+
+ create_parm.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ create_parm.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ create_parm.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ create_parm.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
+ create_parm.in.create_disposition = NTCREATEX_DISP_CREATE;
+ create_parm.in.fname = io->mkdir.in.path;
+ if (create_parm.in.fname[0] == '\\') {
+ create_parm.in.fname++;
+ }
+
+ req = smb2_create_send(tree, &create_parm);
+
+ composite_continue_smb2(ctx, req, continue_mkdir, ctx);
+
+ return ctx;
+}
+
+
+/*
+ composite mkdir call - sync interface
+*/
+NTSTATUS smb2_composite_mkdir(struct smb2_tree *tree, union smb_mkdir *io)
+{
+ struct composite_context *c = smb2_composite_mkdir_send(tree, io);
+ return composite_wait_free(c);
+}
+
+
+
+/*
+ continue after the create in a composite rmdir
+ */
+static void continue_rmdir(struct smb2_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_tree *tree = req->tree;
+ struct smb2_create create_parm;
+ struct smb2_close close_parm;
+ NTSTATUS status;
+
+ status = smb2_create_recv(req, ctx, &create_parm);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(ctx, status);
+ return;
+ }
+
+ ZERO_STRUCT(close_parm);
+ close_parm.in.file.handle = create_parm.out.file.handle;
+
+ req = smb2_close_send(tree, &close_parm);
+ composite_continue_smb2(ctx, req, continue_close, ctx);
+}
+
+/*
+ composite SMB2 rmdir call
+*/
+struct composite_context *smb2_composite_rmdir_send(struct smb2_tree *tree,
+ struct smb_rmdir *io)
+{
+ struct composite_context *ctx;
+ struct smb2_create create_parm;
+ struct smb2_request *req;
+
+ ctx = composite_create(tree, tree->session->transport->socket->event.ctx);
+ if (ctx == NULL) return NULL;
+
+ ZERO_STRUCT(create_parm);
+ create_parm.in.desired_access = SEC_STD_DELETE;
+ create_parm.in.create_disposition = NTCREATEX_DISP_OPEN;
+ create_parm.in.share_access =
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ create_parm.in.create_options =
+ NTCREATEX_OPTIONS_DIRECTORY |
+ NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+ create_parm.in.fname = io->in.path;
+ if (create_parm.in.fname[0] == '\\') {
+ create_parm.in.fname++;
+ }
+
+ req = smb2_create_send(tree, &create_parm);
+
+ composite_continue_smb2(ctx, req, continue_rmdir, ctx);
+ return ctx;
+}
+
+
+/*
+ composite rmdir call - sync interface
+*/
+NTSTATUS smb2_composite_rmdir(struct smb2_tree *tree, struct smb_rmdir *io)
+{
+ struct composite_context *c = smb2_composite_rmdir_send(tree, io);
+ return composite_wait_free(c);
+}
+
+
+/*
+ continue after the setfileinfo in a composite setpathinfo
+ */
+static void continue_setpathinfo_close(struct smb2_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_tree *tree = req->tree;
+ struct smb2_close close_parm;
+ NTSTATUS status;
+ union smb_setfileinfo *io2 = talloc_get_type(ctx->private_data,
+ union smb_setfileinfo);
+
+ status = smb2_setinfo_recv(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(ctx, status);
+ return;
+ }
+
+ ZERO_STRUCT(close_parm);
+ close_parm.in.file.handle = io2->generic.in.file.handle;
+
+ req = smb2_close_send(tree, &close_parm);
+ composite_continue_smb2(ctx, req, continue_close, ctx);
+}
+
+
+/*
+ continue after the create in a composite setpathinfo
+ */
+static void continue_setpathinfo(struct smb2_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_tree *tree = req->tree;
+ struct smb2_create create_parm;
+ NTSTATUS status;
+ union smb_setfileinfo *io2 = talloc_get_type(ctx->private_data,
+ union smb_setfileinfo);
+
+ status = smb2_create_recv(req, ctx, &create_parm);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(ctx, status);
+ return;
+ }
+
+ io2->generic.in.file.handle = create_parm.out.file.handle;
+
+ req = smb2_setinfo_file_send(tree, io2);
+ composite_continue_smb2(ctx, req, continue_setpathinfo_close, ctx);
+}
+
+
+/*
+ composite SMB2 setpathinfo call
+*/
+struct composite_context *smb2_composite_setpathinfo_send(struct smb2_tree *tree,
+ union smb_setfileinfo *io)
+{
+ struct composite_context *ctx;
+ struct smb2_create create_parm;
+ struct smb2_request *req;
+ union smb_setfileinfo *io2;
+
+ ctx = composite_create(tree, tree->session->transport->socket->event.ctx);
+ if (ctx == NULL) return NULL;
+
+ ZERO_STRUCT(create_parm);
+ create_parm.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ create_parm.in.create_disposition = NTCREATEX_DISP_OPEN;
+ create_parm.in.share_access =
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ create_parm.in.create_options = 0;
+ create_parm.in.fname = io->generic.in.file.path;
+ if (create_parm.in.fname[0] == '\\') {
+ create_parm.in.fname++;
+ }
+
+ req = smb2_create_send(tree, &create_parm);
+
+ io2 = talloc(ctx, union smb_setfileinfo);
+ if (composite_nomem(io2, ctx)) {
+ return ctx;
+ }
+ *io2 = *io;
+
+ ctx->private_data = io2;
+
+ composite_continue_smb2(ctx, req, continue_setpathinfo, ctx);
+ return ctx;
+}
+
+
+/*
+ composite setpathinfo call
+ */
+NTSTATUS smb2_composite_setpathinfo(struct smb2_tree *tree, union smb_setfileinfo *io)
+{
+ struct composite_context *c = smb2_composite_setpathinfo_send(tree, io);
+ return composite_wait_free(c);
+}
diff --git a/source4/libcli/smb_composite/smb_composite.h b/source4/libcli/smb_composite/smb_composite.h
new file mode 100644
index 0000000000..a1e1e99d7e
--- /dev/null
+++ b/source4/libcli/smb_composite/smb_composite.h
@@ -0,0 +1,195 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB composite request interfaces
+
+ Copyright (C) Andrew Tridgell 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/>.
+*/
+
+/*
+ this defines the structures associated with "composite"
+ requests. Composite requests are libcli requests that are internally
+ implemented as multiple libcli/raw/ calls, but can be treated as a
+ single call via these composite calls. The composite calls are
+ particularly designed to be used in async applications
+*/
+
+#include "libcli/raw/signing.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+
+
+/*
+ a composite open/read(s)/close request that loads a whole file
+ into memory. Used as a demo of the composite system.
+*/
+struct smb_composite_loadfile {
+ struct {
+ const char *fname;
+ } in;
+ struct {
+ uint8_t *data;
+ uint32_t size;
+ } out;
+};
+
+struct smb_composite_fetchfile {
+ struct {
+ const char *dest_host;
+ const char **ports;
+ const char *called_name;
+ const char *service;
+ const char *service_type;
+ const char *socket_options;
+ struct cli_credentials *credentials;
+ const char *workgroup;
+ const char *filename;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+ struct resolve_context *resolve_ctx;
+ struct smb_iconv_convenience *iconv_convenience;
+ struct gensec_settings *gensec_settings;
+ } in;
+ struct {
+ uint8_t *data;
+ uint32_t size;
+ } out;
+};
+
+/*
+ a composite open/write(s)/close request that saves a whole file from
+ memory. Used as a demo of the composite system.
+*/
+struct smb_composite_savefile {
+ struct {
+ const char *fname;
+ uint8_t *data;
+ uint32_t size;
+ } in;
+};
+
+
+/*
+ a composite request for a full connection to a remote server. Includes
+
+ - socket establishment
+ - session request
+ - negprot
+ - session setup (if credentials are not NULL)
+ - tree connect (if service is not NULL)
+*/
+struct smb_composite_connect {
+ struct {
+ const char *dest_host;
+ const char **dest_ports;
+ const char *socket_options;
+ const char *called_name;
+ const char *service;
+ const char *service_type;
+ struct cli_credentials *credentials;
+ bool fallback_to_anonymous;
+ const char *workgroup;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+ struct smb_iconv_convenience *iconv_convenience;
+ struct gensec_settings *gensec_settings;
+ } in;
+ struct {
+ struct smbcli_tree *tree;
+ bool anonymous_fallback_done;
+ } out;
+};
+
+
+/*
+ generic session setup interface that takes care of which
+ session setup varient to use
+*/
+struct smb_composite_sesssetup {
+ struct {
+ uint32_t sesskey;
+ uint32_t capabilities;
+ struct cli_credentials *credentials;
+ const char *workgroup;
+ struct gensec_settings *gensec_settings;
+ } in;
+ struct {
+ uint16_t vuid;
+ } out;
+};
+
+/*
+ query file system info
+*/
+struct smb_composite_fsinfo {
+ struct {
+ const char *dest_host;
+ const char **dest_ports;
+ const char *socket_options;
+ const char *called_name;
+ const char *service;
+ const char *service_type;
+ struct cli_credentials *credentials;
+ const char *workgroup;
+ enum smb_fsinfo_level level;
+ struct smb_iconv_convenience *iconv_convenience;
+ struct gensec_settings *gensec_settings;
+ } in;
+
+ struct {
+ union smb_fsinfo *fsinfo;
+ } out;
+};
+
+/*
+ composite call for appending new acl to the file's security descriptor and get
+ new full acl
+*/
+
+struct smb_composite_appendacl {
+ struct {
+ const char *fname;
+
+ const struct security_descriptor *sd;
+ } in;
+
+ struct {
+ struct security_descriptor *sd;
+ } out;
+};
+
+/*
+ a composite API to fire connect() calls to multiple targets, picking the
+ first one.
+*/
+
+struct smb_composite_connectmulti {
+ struct {
+ int num_dests;
+ const char **hostnames;
+ const char **addresses;
+ int *ports; /* Either NULL for lp_smb_ports() per
+ * destination or a list of explicit ports */
+ } in;
+ struct {
+ struct smbcli_socket *socket;
+ } out;
+};
+
+struct smbcli_session;
+struct resolve_context;
+
+#include "libcli/smb_composite/proto.h"
diff --git a/source4/libcli/smbc/README b/source4/libcli/smbc/README
new file mode 100644
index 0000000000..66b0782722
--- /dev/null
+++ b/source4/libcli/smbc/README
@@ -0,0 +1 @@
+This is where the new samba4 libsmbclient will live.
diff --git a/source4/libcli/util/clilsa.c b/source4/libcli/util/clilsa.c
new file mode 100644
index 0000000000..16967d73b0
--- /dev/null
+++ b/source4/libcli/util/clilsa.c
@@ -0,0 +1,359 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ lsa calls for file sharing connections
+
+ 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/>.
+*/
+
+/*
+ when dealing with ACLs the file sharing client code needs to
+ sometimes make LSA RPC calls. This code provides an easy interface
+ for doing those calls.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "libcli/util/clilsa.h"
+
+struct smblsa_state {
+ struct dcerpc_pipe *pipe;
+ struct smbcli_tree *ipc_tree;
+ struct policy_handle handle;
+};
+
+/*
+ establish the lsa pipe connection
+*/
+static NTSTATUS smblsa_connect(struct smbcli_state *cli)
+{
+ struct smblsa_state *lsa;
+ NTSTATUS status;
+ struct lsa_OpenPolicy r;
+ uint16_t system_name = '\\';
+ union smb_tcon tcon;
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+
+ if (cli->lsa != NULL) {
+ return NT_STATUS_OK;
+ }
+
+ lsa = talloc(cli, struct smblsa_state);
+ if (lsa == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lsa->ipc_tree = smbcli_tree_init(cli->session, lsa, false);
+ if (lsa->ipc_tree == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* connect to IPC$ */
+ tcon.generic.level = RAW_TCON_TCONX;
+ tcon.tconx.in.flags = 0;
+ tcon.tconx.in.password = data_blob(NULL, 0);
+ tcon.tconx.in.path = "ipc$";
+ tcon.tconx.in.device = "IPC";
+ status = smb_raw_tcon(lsa->ipc_tree, lsa, &tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+ lsa->ipc_tree->tid = tcon.tconx.out.tid;
+
+ lsa->pipe = dcerpc_pipe_init(lsa, cli->transport->socket->event.ctx,
+ cli->transport->iconv_convenience);
+ if (lsa->pipe == NULL) {
+ talloc_free(lsa);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* open the LSA pipe */
+ status = dcerpc_pipe_open_smb(lsa->pipe, lsa->ipc_tree, NDR_LSARPC_NAME);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+
+ /* bind to the LSA pipe */
+ status = dcerpc_bind_auth_none(lsa->pipe, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+
+
+ /* open a lsa policy handle */
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ r.in.system_name = &system_name;
+ r.in.attr = &attr;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &lsa->handle;
+
+ status = dcerpc_lsa_OpenPolicy(lsa->pipe, lsa, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+
+ cli->lsa = lsa;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return the set of privileges for the given sid
+*/
+NTSTATUS smblsa_sid_privileges(struct smbcli_state *cli, struct dom_sid *sid,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RightSet *rights)
+{
+ NTSTATUS status;
+ struct lsa_EnumAccountRights r;
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.sid = sid;
+ r.out.rights = rights;
+
+ return dcerpc_lsa_EnumAccountRights(cli->lsa->pipe, mem_ctx, &r);
+}
+
+
+/*
+ check if a named sid has a particular named privilege
+*/
+NTSTATUS smblsa_sid_check_privilege(struct smbcli_state *cli,
+ const char *sid_str,
+ const char *privilege)
+{
+ struct lsa_RightSet rights;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = talloc_new(cli);
+ struct dom_sid *sid;
+ unsigned i;
+
+ sid = dom_sid_parse_talloc(mem_ctx, sid_str);
+ if (sid == NULL) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_INVALID_SID;
+ }
+
+ status = smblsa_sid_privileges(cli, sid, mem_ctx, &rights);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ for (i=0;i<rights.count;i++) {
+ if (strcmp(rights.names[i].string, privilege) == 0) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+}
+
+
+/*
+ lookup a SID, returning its name
+*/
+NTSTATUS smblsa_lookup_sid(struct smbcli_state *cli,
+ const char *sid_str,
+ TALLOC_CTX *mem_ctx,
+ const char **name)
+{
+ struct lsa_LookupSids r;
+ struct lsa_TransNameArray names;
+ struct lsa_SidArray sids;
+ struct lsa_RefDomainList *domains = NULL;
+ uint32_t count = 1;
+ NTSTATUS status;
+ struct dom_sid *sid;
+ TALLOC_CTX *mem_ctx2 = talloc_new(mem_ctx);
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ sid = dom_sid_parse_talloc(mem_ctx2, sid_str);
+ if (sid == NULL) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ names.count = 0;
+ names.names = NULL;
+
+ sids.num_sids = 1;
+ sids.sids = talloc(mem_ctx2, struct lsa_SidPtr);
+ sids.sids[0].sid = sid;
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.sids = &sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.names = &names;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupSids(cli->lsa->pipe, mem_ctx2, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx2);
+ return status;
+ }
+ if (names.count != 1) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ (*name) = talloc_asprintf(mem_ctx, "%s\\%s",
+ domains->domains[0].name.string,
+ names.names[0].name.string);
+
+ talloc_free(mem_ctx2);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lookup a name, returning its sid
+*/
+NTSTATUS smblsa_lookup_name(struct smbcli_state *cli,
+ const char *name,
+ TALLOC_CTX *mem_ctx,
+ const char **sid_str)
+{
+ struct lsa_LookupNames r;
+ struct lsa_TransSidArray sids;
+ struct lsa_String names;
+ struct lsa_RefDomainList *domains = NULL;
+ uint32_t count = 1;
+ NTSTATUS status;
+ struct dom_sid *sid;
+ TALLOC_CTX *mem_ctx2 = talloc_new(mem_ctx);
+ uint32_t rid;
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names.string = name;
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.num_names = 1;
+ r.in.names = &names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.sids = &sids;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupNames(cli->lsa->pipe, mem_ctx2, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx2);
+ return status;
+ }
+ if (sids.count != 1) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ sid = domains->domains[0].sid;
+ rid = sids.sids[0].rid;
+
+ (*sid_str) = talloc_asprintf(mem_ctx, "%s-%u",
+ dom_sid_string(mem_ctx2, sid), rid);
+
+ talloc_free(mem_ctx2);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ add a set of privileges to the given sid
+*/
+NTSTATUS smblsa_sid_add_privileges(struct smbcli_state *cli, struct dom_sid *sid,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RightSet *rights)
+{
+ NTSTATUS status;
+ struct lsa_AddAccountRights r;
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.sid = sid;
+ r.in.rights = rights;
+
+ return dcerpc_lsa_AddAccountRights(cli->lsa->pipe, mem_ctx, &r);
+}
+
+/*
+ remove a set of privileges from the given sid
+*/
+NTSTATUS smblsa_sid_del_privileges(struct smbcli_state *cli, struct dom_sid *sid,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RightSet *rights)
+{
+ NTSTATUS status;
+ struct lsa_RemoveAccountRights r;
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.sid = sid;
+ r.in.remove_all = 0;
+ r.in.rights = rights;
+
+ return dcerpc_lsa_RemoveAccountRights(cli->lsa->pipe, mem_ctx, &r);
+}
diff --git a/source4/libcli/util/errormap.c b/source4/libcli/util/errormap.c
new file mode 100644
index 0000000000..930e45b214
--- /dev/null
+++ b/source4/libcli/util/errormap.c
@@ -0,0 +1,1408 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * error mapping functions
+ * Copyright (C) Andrew Tridgell 2001
+ * Copyright (C) Andrew Bartlett 2001
+ * Copyright (C) Tim Potter 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 "librpc/ndr/libndr.h"
+
+/* This map was extracted by the ERRMAPEXTRACT smbtorture command.
+ The setup was a Samba HEAD (2002-01-03) PDC and an Win2k member
+ workstation. The PDC was modified (by using the 'name_to_nt_status'
+ authentication module) to convert the username (in hex) into the
+ corresponding NTSTATUS error return.
+
+ By opening two nbt sessions to the Win2k workstation, one negotiating
+ DOS and one negotiating NT errors it was possible to extract the
+ error mapping. (Because the server only supplies NT errors, the
+ NT4 workstation had to use its own error tables to convert these
+ to dos errors).
+
+ Some errors show up as 'squashed' because the NT error connection
+ got back a different error to the one it sent, so a mapping could
+ not be determined (a guess has been made in this case, to map the
+ error as squashed). This is done mainly to prevent users from getting
+ NT_STATUS_WRONG_PASSWORD and NT_STATUS_NO_SUCH_USER errors (they get
+ NT_STATUS_LOGON_FAILURE instead.
+
+ -- abartlet (2002-01-03)
+*/
+
+/* NT status -> dos error map */
+static const struct {
+ uint8_t dos_class;
+ uint32_t dos_code;
+ NTSTATUS ntstatus;
+} ntstatus_to_dos_map[] = {
+ {ERRDOS, ERRnofiles, STATUS_NO_MORE_FILES},
+ {ERRDOS, ERRnofiles, NT_STATUS_NO_MORE_ENTRIES},
+ {ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL},
+ {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRDOS, 87, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA},
+ {ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK},
+ {ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC},
+ {ERRDOS, 87, NT_STATUS_INVALID_CID},
+ {ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER},
+ {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE},
+ {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE},
+ {ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST},
+ {ERRDOS, 38, NT_STATUS_END_OF_FILE},
+ {ERRDOS, 34, NT_STATUS_WRONG_VOLUME},
+ {ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA},
+ {ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR},
+/** Session setup succeeded. This shouldn't happen...*/
+/** Session setup succeeded. This shouldn't happen...*/
+/** NT error on DOS connection! (NT_STATUS_OK) */
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK
+ during the session setup }
+*/
+#if 0
+ {SUCCESS, 0, NT_STATUS_OK},
+#endif
+ {ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY},
+ {ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES},
+ {ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW},
+ {ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM},
+ {ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION},
+ {ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION},
+ {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE},
+ {ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
+ during the session setup }
+*/
+ {ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED},
+ {ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL},
+ {ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNWIND},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET},
+ {ERRDOS, 158, NT_STATUS_NOT_LOCKED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR},
+ {ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM},
+ {ERRDOS, 487, NT_STATUS_NOT_COMMITTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES},
+ {ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR},
+ {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND},
+ {ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE},
+ {ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED},
+ {ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID},
+ {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD},
+ {ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN},
+ {ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR},
+ {ERRDOS, 23, NT_STATUS_DATA_ERROR},
+ {ERRDOS, 23, NT_STATUS_CRC_ERROR},
+ {ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG},
+ {ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED},
+ {ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE},
+ {ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED},
+ {ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION},
+ {ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED},
+ {ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+ {ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET},
+ {ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE},
+ {ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING},
+ {ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT},
+ {ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP},
+ {ERRDOS, 87, NT_STATUS_SECTION_PROTECTION},
+ {ERRDOS, 282, NT_STATUS_EAS_NOT_SUPPORTED},
+ {ERRDOS, 255, NT_STATUS_EA_TOO_LARGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR},
+ {ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT},
+ {ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_DELETE_PENDING},
+ {ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION},
+ {ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY},
+ {ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME},
+ {ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE
+ during the session setup }
+*/
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER},
+ {ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE
+ during the session setup }
+*/
+ {ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD},
+ {ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION},
+ {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION},
+ {ERRSRV, 2241, NT_STATUS_INVALID_LOGON_HOURS},
+ {ERRSRV, 2240, NT_STATUS_INVALID_WORKSTATION},
+ {ERRSRV, 2242, NT_STATUS_PASSWORD_EXPIRED},
+ {ERRSRV, 2239, NT_STATUS_ACCOUNT_DISABLED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR},
+ {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL},
+ {ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED},
+ {ERRDOS, 112, NT_STATUS_DISK_FULL},
+ {ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+ {ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY},
+ {ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED},
+ {ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL},
+ {ERRDOS, ERRres, NT_STATUS_SECTION_NOT_EXTENDED},
+ {ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO},
+ {ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION},
+ {ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES},
+ {ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID},
+ {ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_INSUFFICIENT_RESOURCES to NT_STATUS_INSUFF_SERVER_RESOURCES
+ during the session setup }
+*/
+ {ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND},
+ {ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED},
+ {ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE},
+ {ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE},
+ {ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA},
+ {ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED},
+ {ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE},
+ {ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD},
+ {ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT},
+ {ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE},
+ {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE},
+ {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE},
+ {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY},
+ {ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION},
+ {ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED},
+ {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING},
+ {ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING},
+ {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE},
+ {ERRDOS, 121, NT_STATUS_IO_TIMEOUT},
+ {ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED},
+ {ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET},
+ {ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY},
+ {ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED},
+ {ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING},
+ {ERRDOS, 52, NT_STATUS_DUPLICATE_NAME},
+ {ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH},
+ {ERRDOS, 54, NT_STATUS_NETWORK_BUSY},
+ {ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST},
+ {ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS},
+ {ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR},
+ {ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE},
+ {ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+ {ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER},
+ {ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL},
+ {ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE},
+ {ERRDOS, 63, NT_STATUS_PRINT_CANCELLED},
+ {ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED},
+ {ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES},
+ {ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS},
+ {ERRDOS, 70, NT_STATUS_SHARING_PAUSED},
+ {ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED},
+ {ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT},
+ {ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED},
+ {ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT},
+ {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED},
+ {ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED},
+ {ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12},
+ {ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE},
+ {ERRDOS, 203, NT_STATUS(0xc0000100)},
+ {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR},
+ {ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION},
+ {ERRDOS, 206, NT_STATUS_NAME_TOO_LONG},
+ {ERRDOS, 2401, NT_STATUS_FILES_OPEN},
+ {ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE},
+ {ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND},
+ {ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_LDT},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT},
+ {ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO},
+ {ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANCELLED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME},
+ {ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT},
+ {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP},
+ {ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS},
+ {ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED},
+ {ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT},
+ {ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC},
+ {ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED},
+ {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED},
+ {ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND},
+ {ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT},
+ {ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT},
+ {ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT},
+ {ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES},
+ {ERRDOS, 59, NT_STATUS_LINK_FAILED},
+ {ERRDOS, 59, NT_STATUS_LINK_TIMEOUT},
+ {ERRDOS, 59, NT_STATUS_INVALID_CONNECTION},
+ {ERRDOS, 59, NT_STATUS_INVALID_ADDRESS},
+ {ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE},
+ {ERRDOS, 124, NT_STATUS_INVALID_LEVEL},
+ {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT},
+ {ERRDOS, 109, NT_STATUS_PIPE_BROKEN},
+ {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT},
+ {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME},
+ {ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS},
+ {ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS},
+ {ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS},
+ {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY},
+ {ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000016e)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000016f)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc0000170)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc0000171)},
+ {ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA},
+ {ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc0000179)},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER},
+ {ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS},
+ {ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN},
+ {ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE},
+ {ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR},
+ {ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL},
+ {ERRDOS, 19, NT_STATUS_TOO_LATE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_NO_TRUST_SAM_ACCOUNT to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
+ during the session setup }
+*/
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT},
+ {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START},
+ {ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED},
+ {ERRDOS, ERRinvgroup, NT_STATUS_NETLOGON_NOT_STARTED},
+ {ERRSRV, 2239, NT_STATUS_ACCOUNT_EXPIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK},
+ {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT},
+ {ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT},
+ {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE
+ during the session setup }
+*/
+ {ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY},
+ {ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND},
+ {ERRDOS, ERRnomem, NT_STATUS_INSUFF_SERVER_RESOURCES},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES},
+ {ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS},
+ {ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED},
+ {ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED},
+ {ERRDOS, 64, NT_STATUS_CONNECTION_RESET},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_NODES},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE},
+ {ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION},
+ {ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT},
+ {ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT},
+ {ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA},
+ {ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID},
+ {ERRSRV, 2242, NT_STATUS_PASSWORD_MUST_CHANGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM},
+ {ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ},
+ {ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK},
+ {ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID},
+ {ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_RETRY},
+ {ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT},
+ {ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT},
+ {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER},
+ {ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION},
+ {ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024a)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024b)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024c)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024d)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024e)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024f)},
+ {ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT},
+ {ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST},
+ {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1},
+ {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2},
+ {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT},
+ {ERRSRV, ERRbadtype, NT_STATUS_PATH_NOT_COVERED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE},
+ {ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT},
+ {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000025d)},
+ {ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH},
+ {ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND},
+ {ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND},
+ {ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LINKS},
+ {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE},
+ {ERRDOS, 21, NT_STATUS(0xc000026e)},
+ {ERRDOS, 161, NT_STATUS(0xc0000281)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028a)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028b)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000028c)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028d)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028e)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028f)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc0000290)},
+ {ERRDOS, ERRbadfunc, NT_STATUS(0xc000029c)},
+};
+
+
+/* errmap NTSTATUS->Win32 */
+static const struct {
+ NTSTATUS ntstatus;
+ WERROR werror;
+} ntstatus_to_werror_map[] = {
+ /*
+ * we add this manualy here, so that W_ERROR(0x5)
+ * gets mapped to NTSTATUS_ACCESS_DENIED
+ */
+ {NT_STATUS_ACCESS_DENIED, WERR_ACCESS_DENIED},
+ {NT_STATUS(0x103), W_ERROR(0x3e5)},
+ {NT_STATUS(0x105), W_ERROR(0xea)},
+ {NT_STATUS(0x106), W_ERROR(0x514)},
+ {NT_STATUS(0x107), W_ERROR(0x515)},
+ {NT_STATUS(0x10c), W_ERROR(0x3fe)},
+ {NT_STATUS(0x10d), W_ERROR(0x516)},
+ {NT_STATUS(0x121), W_ERROR(0x2009)},
+ {NT_STATUS(0xc0000001), W_ERROR(0x1f)},
+ {NT_STATUS(0xc0000002), W_ERROR(0x1)},
+ {NT_STATUS(0xc0000003), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000004), W_ERROR(0x18)},
+ {NT_STATUS(0xc0000005), W_ERROR(0x3e6)},
+ {NT_STATUS(0xc0000006), W_ERROR(0x3e7)},
+ {NT_STATUS(0xc0000007), W_ERROR(0x5ae)},
+ {NT_STATUS(0xc0000008), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000009), W_ERROR(0x3e9)},
+ {NT_STATUS(0xc000000a), W_ERROR(0xc1)},
+ {NT_STATUS(0xc000000b), W_ERROR(0x57)},
+ {NT_STATUS(0xc000000d), W_ERROR(0x57)},
+ {NT_STATUS(0xc000000e), W_ERROR(0x2)},
+ {NT_STATUS(0xc000000f), W_ERROR(0x2)},
+ {NT_STATUS(0xc0000010), W_ERROR(0x1)},
+ {NT_STATUS(0xc0000011), W_ERROR(0x26)},
+ {NT_STATUS(0xc0000012), W_ERROR(0x22)},
+ {NT_STATUS(0xc0000013), W_ERROR(0x15)},
+ {NT_STATUS(0xc0000014), W_ERROR(0x6f9)},
+ {NT_STATUS(0xc0000015), W_ERROR(0x1b)},
+ {NT_STATUS(0xc0000016), W_ERROR(0xea)},
+ {NT_STATUS(0xc0000017), W_ERROR(0x8)},
+ {NT_STATUS(0xc0000018), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc0000019), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc000001a), W_ERROR(0x57)},
+ {NT_STATUS(0xc000001b), W_ERROR(0x57)},
+ {NT_STATUS(0xc000001c), W_ERROR(0x1)},
+ {NT_STATUS(0xc000001d), W_ERROR(0xc000001d)},
+ {NT_STATUS(0xc000001e), W_ERROR(0x5)},
+ {NT_STATUS(0xc000001f), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000020), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000021), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000022), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000023), W_ERROR(0x7a)},
+ {NT_STATUS(0xc0000024), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000025), W_ERROR(0xc0000025)},
+ {NT_STATUS(0xc0000026), W_ERROR(0xc0000026)},
+ {NT_STATUS(0xc000002a), W_ERROR(0x9e)},
+ {NT_STATUS(0xc000002b), W_ERROR(0xc000002b)},
+ {NT_STATUS(0xc000002c), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc000002d), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc0000030), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000032), W_ERROR(0x571)},
+ {NT_STATUS(0xc0000033), W_ERROR(0x7b)},
+ {NT_STATUS(0xc0000034), W_ERROR(0x2)},
+ {NT_STATUS(0xc0000035), W_ERROR(0xb7)},
+ {NT_STATUS(0xc0000037), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000039), W_ERROR(0xa1)},
+ {NT_STATUS(0xc000003a), W_ERROR(0x3)},
+ {NT_STATUS(0xc000003b), W_ERROR(0xa1)},
+ {NT_STATUS(0xc000003c), W_ERROR(0x45d)},
+ {NT_STATUS(0xc000003d), W_ERROR(0x45d)},
+ {NT_STATUS(0xc000003e), W_ERROR(0x17)},
+ {NT_STATUS(0xc000003f), W_ERROR(0x17)},
+ {NT_STATUS(0xc0000040), W_ERROR(0x8)},
+ {NT_STATUS(0xc0000041), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000042), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000043), W_ERROR(0x20)},
+ {NT_STATUS(0xc0000044), W_ERROR(0x718)},
+ {NT_STATUS(0xc0000045), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000046), W_ERROR(0x120)},
+ {NT_STATUS(0xc0000047), W_ERROR(0x12a)},
+ {NT_STATUS(0xc0000048), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000049), W_ERROR(0x57)},
+ {NT_STATUS(0xc000004a), W_ERROR(0x9c)},
+ {NT_STATUS(0xc000004b), W_ERROR(0x5)},
+ {NT_STATUS(0xc000004c), W_ERROR(0x57)},
+ {NT_STATUS(0xc000004d), W_ERROR(0x57)},
+ {NT_STATUS(0xc000004e), W_ERROR(0x57)},
+ {NT_STATUS(0xc000004f), W_ERROR(0x11a)},
+ {NT_STATUS(0xc0000050), W_ERROR(0xff)},
+ {NT_STATUS(0xc0000051), W_ERROR(0x570)},
+ {NT_STATUS(0xc0000052), W_ERROR(0x570)},
+ {NT_STATUS(0xc0000053), W_ERROR(0x570)},
+ {NT_STATUS(0xc0000054), W_ERROR(0x21)},
+ {NT_STATUS(0xc0000055), W_ERROR(0x21)},
+ {NT_STATUS(0xc0000056), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000057), W_ERROR(0x32)},
+ {NT_STATUS(0xc0000058), W_ERROR(0x519)},
+ {NT_STATUS(0xc0000059), W_ERROR(0x51a)},
+ {NT_STATUS(0xc000005a), W_ERROR(0x51b)},
+ {NT_STATUS(0xc000005b), W_ERROR(0x51c)},
+ {NT_STATUS(0xc000005c), W_ERROR(0x51d)},
+ {NT_STATUS(0xc000005d), W_ERROR(0x51e)},
+ {NT_STATUS(0xc000005e), W_ERROR(0x51f)},
+ {NT_STATUS(0xc000005f), W_ERROR(0x520)},
+ {NT_STATUS(0xc0000060), W_ERROR(0x521)},
+ {NT_STATUS(0xc0000061), W_ERROR(0x522)},
+ {NT_STATUS(0xc0000062), W_ERROR(0x523)},
+ {NT_STATUS(0xc0000063), W_ERROR(0x524)},
+ {NT_STATUS(0xc0000064), W_ERROR(0x525)},
+ {NT_STATUS(0xc0000065), W_ERROR(0x526)},
+ {NT_STATUS(0xc0000066), W_ERROR(0x527)},
+ {NT_STATUS(0xc0000067), W_ERROR(0x528)},
+ {NT_STATUS(0xc0000068), W_ERROR(0x529)},
+ {NT_STATUS(0xc0000069), W_ERROR(0x52a)},
+ {NT_STATUS(0xc000006a), W_ERROR(0x56)},
+ {NT_STATUS(0xc000006b), W_ERROR(0x52c)},
+ {NT_STATUS(0xc000006c), W_ERROR(0x52d)},
+ {NT_STATUS(0xc000006d), W_ERROR(0x52e)},
+ {NT_STATUS(0xc000006e), W_ERROR(0x52f)},
+ {NT_STATUS(0xc000006f), W_ERROR(0x530)},
+ {NT_STATUS(0xc0000070), W_ERROR(0x531)},
+ {NT_STATUS(0xc0000071), W_ERROR(0x532)},
+ {NT_STATUS(0xc0000072), W_ERROR(0x533)},
+ {NT_STATUS(0xc0000073), W_ERROR(0x534)},
+ {NT_STATUS(0xc0000074), W_ERROR(0x535)},
+ {NT_STATUS(0xc0000075), W_ERROR(0x536)},
+ {NT_STATUS(0xc0000076), W_ERROR(0x537)},
+ {NT_STATUS(0xc0000077), W_ERROR(0x538)},
+ {NT_STATUS(0xc0000078), W_ERROR(0x539)},
+ {NT_STATUS(0xc0000079), W_ERROR(0x53a)},
+ {NT_STATUS(0xc000007a), W_ERROR(0x7f)},
+ {NT_STATUS(0xc000007b), W_ERROR(0xc1)},
+ {NT_STATUS(0xc000007c), W_ERROR(0x3f0)},
+ {NT_STATUS(0xc000007d), W_ERROR(0x53c)},
+ {NT_STATUS(0xc000007e), W_ERROR(0x9e)},
+ {NT_STATUS(0xc000007f), W_ERROR(0x70)},
+ {NT_STATUS(0xc0000080), W_ERROR(0x53d)},
+ {NT_STATUS(0xc0000081), W_ERROR(0x53e)},
+ {NT_STATUS(0xc0000082), W_ERROR(0x44)},
+ {NT_STATUS(0xc0000083), W_ERROR(0x103)},
+ {NT_STATUS(0xc0000084), W_ERROR(0x53f)},
+ {NT_STATUS(0xc0000085), W_ERROR(0x103)},
+ {NT_STATUS(0xc0000086), W_ERROR(0x9a)},
+ {NT_STATUS(0xc0000087), W_ERROR(0xe)},
+ {NT_STATUS(0xc0000088), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc0000089), W_ERROR(0x714)},
+ {NT_STATUS(0xc000008a), W_ERROR(0x715)},
+ {NT_STATUS(0xc000008b), W_ERROR(0x716)},
+ {NT_STATUS(0xc000008c), W_ERROR(0xc000008c)},
+ {NT_STATUS(0xc000008d), W_ERROR(0xc000008d)},
+ {NT_STATUS(0xc000008e), W_ERROR(0xc000008e)},
+ {NT_STATUS(0xc000008f), W_ERROR(0xc000008f)},
+ {NT_STATUS(0xc0000090), W_ERROR(0xc0000090)},
+ {NT_STATUS(0xc0000091), W_ERROR(0xc0000091)},
+ {NT_STATUS(0xc0000092), W_ERROR(0xc0000092)},
+ {NT_STATUS(0xc0000093), W_ERROR(0xc0000093)},
+ {NT_STATUS(0xc0000094), W_ERROR(0xc0000094)},
+ {NT_STATUS(0xc0000095), W_ERROR(0x216)},
+ {NT_STATUS(0xc0000096), W_ERROR(0xc0000096)},
+ {NT_STATUS(0xc0000097), W_ERROR(0x8)},
+ {NT_STATUS(0xc0000098), W_ERROR(0x3ee)},
+ {NT_STATUS(0xc0000099), W_ERROR(0x540)},
+ {NT_STATUS(0xc000009a), W_ERROR(0x5aa)},
+ {NT_STATUS(0xc000009b), W_ERROR(0x3)},
+ {NT_STATUS(0xc000009c), W_ERROR(0x17)},
+ {NT_STATUS(0xc000009d), W_ERROR(0x48f)},
+ {NT_STATUS(0xc000009e), W_ERROR(0x15)},
+ {NT_STATUS(0xc000009f), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc00000a0), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc00000a1), W_ERROR(0x5ad)},
+ {NT_STATUS(0xc00000a2), W_ERROR(0x13)},
+ {NT_STATUS(0xc00000a3), W_ERROR(0x15)},
+ {NT_STATUS(0xc00000a4), W_ERROR(0x541)},
+ {NT_STATUS(0xc00000a5), W_ERROR(0x542)},
+ {NT_STATUS(0xc00000a6), W_ERROR(0x543)},
+ {NT_STATUS(0xc00000a7), W_ERROR(0x544)},
+ {NT_STATUS(0xc00000a8), W_ERROR(0x545)},
+ {NT_STATUS(0xc00000a9), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000ab), W_ERROR(0xe7)},
+ {NT_STATUS(0xc00000ac), W_ERROR(0xe7)},
+ {NT_STATUS(0xc00000ad), W_ERROR(0xe6)},
+ {NT_STATUS(0xc00000ae), W_ERROR(0xe7)},
+ {NT_STATUS(0xc00000af), W_ERROR(0x1)},
+ {NT_STATUS(0xc00000b0), W_ERROR(0xe9)},
+ {NT_STATUS(0xc00000b1), W_ERROR(0xe8)},
+ {NT_STATUS(0xc00000b2), W_ERROR(0x217)},
+ {NT_STATUS(0xc00000b3), W_ERROR(0x218)},
+ {NT_STATUS(0xc00000b4), W_ERROR(0xe6)},
+ {NT_STATUS(0xc00000b5), W_ERROR(0x79)},
+ {NT_STATUS(0xc00000b6), W_ERROR(0x26)},
+ {NT_STATUS(0xc00000ba), W_ERROR(0x5)},
+ {NT_STATUS(0xc00000bb), W_ERROR(0x32)},
+ {NT_STATUS(0xc00000bc), W_ERROR(0x33)},
+ {NT_STATUS(0xc00000bd), W_ERROR(0x34)},
+ {NT_STATUS(0xc00000be), W_ERROR(0x35)},
+ {NT_STATUS(0xc00000bf), W_ERROR(0x36)},
+ {NT_STATUS(0xc00000c0), W_ERROR(0x37)},
+ {NT_STATUS(0xc00000c1), W_ERROR(0x38)},
+ {NT_STATUS(0xc00000c2), W_ERROR(0x39)},
+ {NT_STATUS(0xc00000c3), W_ERROR(0x3a)},
+ {NT_STATUS(0xc00000c4), W_ERROR(0x3b)},
+ {NT_STATUS(0xc00000c5), W_ERROR(0x3c)},
+ {NT_STATUS(0xc00000c6), W_ERROR(0x3d)},
+ {NT_STATUS(0xc00000c7), W_ERROR(0x3e)},
+ {NT_STATUS(0xc00000c8), W_ERROR(0x3f)},
+ {NT_STATUS(0xc00000c9), W_ERROR(0x40)},
+ {NT_STATUS(0xc00000ca), W_ERROR(0x41)},
+ {NT_STATUS(0xc00000cb), W_ERROR(0x42)},
+ {NT_STATUS(0xc00000cc), W_ERROR(0x43)},
+ {NT_STATUS(0xc00000cd), W_ERROR(0x44)},
+ {NT_STATUS(0xc00000ce), W_ERROR(0x45)},
+ {NT_STATUS(0xc00000cf), W_ERROR(0x46)},
+ {NT_STATUS(0xc00000d0), W_ERROR(0x47)},
+ {NT_STATUS(0xc00000d1), W_ERROR(0x48)},
+ {NT_STATUS(0xc00000d2), W_ERROR(0x58)},
+ {NT_STATUS(0xc00000d4), W_ERROR(0x11)},
+ {NT_STATUS(0xc00000d5), W_ERROR(0x5)},
+ {NT_STATUS(0xc00000d6), W_ERROR(0xf0)},
+ {NT_STATUS(0xc00000d7), W_ERROR(0x546)},
+ {NT_STATUS(0xc00000d9), W_ERROR(0xe8)},
+ {NT_STATUS(0xc00000da), W_ERROR(0x547)},
+ {NT_STATUS(0xc00000dc), W_ERROR(0x548)},
+ {NT_STATUS(0xc00000dd), W_ERROR(0x549)},
+ {NT_STATUS(0xc00000de), W_ERROR(0x54a)},
+ {NT_STATUS(0xc00000df), W_ERROR(0x54b)},
+ {NT_STATUS(0xc00000e0), W_ERROR(0x54c)},
+ {NT_STATUS(0xc00000e1), W_ERROR(0x54d)},
+ {NT_STATUS(0xc00000e2), W_ERROR(0x12c)},
+ {NT_STATUS(0xc00000e3), W_ERROR(0x12d)},
+ {NT_STATUS(0xc00000e4), W_ERROR(0x54e)},
+ {NT_STATUS(0xc00000e5), W_ERROR(0x54f)},
+ {NT_STATUS(0xc00000e6), W_ERROR(0x550)},
+ {NT_STATUS(0xc00000e7), W_ERROR(0x551)},
+ {NT_STATUS(0xc00000e8), W_ERROR(0x6f8)},
+ {NT_STATUS(0xc00000ed), W_ERROR(0x552)},
+ {NT_STATUS(0xc00000ee), W_ERROR(0x553)},
+ {NT_STATUS(0xc00000ef), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f0), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f1), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f2), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f3), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f4), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f5), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f6), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f7), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f8), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f9), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000fa), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000fb), W_ERROR(0x3)},
+ {NT_STATUS(0xc00000fd), W_ERROR(0x3e9)},
+ {NT_STATUS(0xc00000fe), W_ERROR(0x554)},
+ {NT_STATUS(0xc0000100), W_ERROR(0xcb)},
+ {NT_STATUS(0xc0000101), W_ERROR(0x91)},
+ {NT_STATUS(0xc0000102), W_ERROR(0x570)},
+ {NT_STATUS(0xc0000103), W_ERROR(0x10b)},
+ {NT_STATUS(0xc0000104), W_ERROR(0x555)},
+ {NT_STATUS(0xc0000105), W_ERROR(0x556)},
+ {NT_STATUS(0xc0000106), W_ERROR(0xce)},
+ {NT_STATUS(0xc0000107), W_ERROR(0x961)},
+ {NT_STATUS(0xc0000108), W_ERROR(0x964)},
+ {NT_STATUS(0xc000010a), W_ERROR(0x5)},
+ {NT_STATUS(0xc000010b), W_ERROR(0x557)},
+ {NT_STATUS(0xc000010d), W_ERROR(0x558)},
+ {NT_STATUS(0xc000010e), W_ERROR(0x420)},
+ {NT_STATUS(0xc0000117), W_ERROR(0x5a4)},
+ {NT_STATUS(0xc000011b), W_ERROR(0xc1)},
+ {NT_STATUS(0xc000011c), W_ERROR(0x559)},
+ {NT_STATUS(0xc000011d), W_ERROR(0x55a)},
+ {NT_STATUS(0xc000011e), W_ERROR(0x3ee)},
+ {NT_STATUS(0xc000011f), W_ERROR(0x4)},
+ {NT_STATUS(0xc0000120), W_ERROR(0x3e3)},
+ {NT_STATUS(0xc0000121), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000122), W_ERROR(0x4ba)},
+ {NT_STATUS(0xc0000123), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000124), W_ERROR(0x55b)},
+ {NT_STATUS(0xc0000125), W_ERROR(0x55c)},
+ {NT_STATUS(0xc0000126), W_ERROR(0x55d)},
+ {NT_STATUS(0xc0000127), W_ERROR(0x55e)},
+ {NT_STATUS(0xc0000128), W_ERROR(0x6)},
+ {NT_STATUS(0xc000012b), W_ERROR(0x55f)},
+ {NT_STATUS(0xc000012d), W_ERROR(0x5af)},
+ {NT_STATUS(0xc000012e), W_ERROR(0xc1)},
+ {NT_STATUS(0xc000012f), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000130), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000131), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000133), W_ERROR(0x576)},
+ {NT_STATUS(0xc0000135), W_ERROR(0x7e)},
+ {NT_STATUS(0xc0000138), W_ERROR(0xb6)},
+ {NT_STATUS(0xc0000139), W_ERROR(0x7f)},
+ {NT_STATUS(0xc000013b), W_ERROR(0x40)},
+ {NT_STATUS(0xc000013c), W_ERROR(0x40)},
+ {NT_STATUS(0xc000013d), W_ERROR(0x33)},
+ {NT_STATUS(0xc000013e), W_ERROR(0x3b)},
+ {NT_STATUS(0xc000013f), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000140), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000141), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000142), W_ERROR(0x45a)},
+ {NT_STATUS(0xc0000148), W_ERROR(0x7c)},
+ {NT_STATUS(0xc0000149), W_ERROR(0x56)},
+ {NT_STATUS(0xc000014b), W_ERROR(0x6d)},
+ {NT_STATUS(0xc000014c), W_ERROR(0x3f1)},
+ {NT_STATUS(0xc000014d), W_ERROR(0x3f8)},
+ {NT_STATUS(0xc000014f), W_ERROR(0x3ed)},
+ {NT_STATUS(0xc0000150), W_ERROR(0x45e)},
+ {NT_STATUS(0xc0000151), W_ERROR(0x560)},
+ {NT_STATUS(0xc0000152), W_ERROR(0x561)},
+ {NT_STATUS(0xc0000153), W_ERROR(0x562)},
+ {NT_STATUS(0xc0000154), W_ERROR(0x563)},
+ {NT_STATUS(0xc0000155), W_ERROR(0x564)},
+ {NT_STATUS(0xc0000156), W_ERROR(0x565)},
+ {NT_STATUS(0xc0000157), W_ERROR(0x566)},
+ {NT_STATUS(0xc0000158), W_ERROR(0x567)},
+ {NT_STATUS(0xc0000159), W_ERROR(0x3ef)},
+ {NT_STATUS(0xc000015a), W_ERROR(0x568)},
+ {NT_STATUS(0xc000015b), W_ERROR(0x569)},
+ {NT_STATUS(0xc000015c), W_ERROR(0x3f9)},
+ {NT_STATUS(0xc000015d), W_ERROR(0x56a)},
+ {NT_STATUS(0xc000015f), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000162), W_ERROR(0x459)},
+ {NT_STATUS(0xc0000165), W_ERROR(0x462)},
+ {NT_STATUS(0xc0000166), W_ERROR(0x463)},
+ {NT_STATUS(0xc0000167), W_ERROR(0x464)},
+ {NT_STATUS(0xc0000168), W_ERROR(0x465)},
+ {NT_STATUS(0xc0000169), W_ERROR(0x466)},
+ {NT_STATUS(0xc000016a), W_ERROR(0x467)},
+ {NT_STATUS(0xc000016b), W_ERROR(0x468)},
+ {NT_STATUS(0xc000016c), W_ERROR(0x45f)},
+ {NT_STATUS(0xc000016d), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000172), W_ERROR(0x451)},
+ {NT_STATUS(0xc0000173), W_ERROR(0x452)},
+ {NT_STATUS(0xc0000174), W_ERROR(0x453)},
+ {NT_STATUS(0xc0000175), W_ERROR(0x454)},
+ {NT_STATUS(0xc0000176), W_ERROR(0x455)},
+ {NT_STATUS(0xc0000177), W_ERROR(0x469)},
+ {NT_STATUS(0xc0000178), W_ERROR(0x458)},
+ {NT_STATUS(0xc000017a), W_ERROR(0x56b)},
+ {NT_STATUS(0xc000017b), W_ERROR(0x56c)},
+ {NT_STATUS(0xc000017c), W_ERROR(0x3fa)},
+ {NT_STATUS(0xc000017d), W_ERROR(0x3fb)},
+ {NT_STATUS(0xc000017e), W_ERROR(0x56d)},
+ {NT_STATUS(0xc000017f), W_ERROR(0x56e)},
+ {NT_STATUS(0xc0000180), W_ERROR(0x3fc)},
+ {NT_STATUS(0xc0000181), W_ERROR(0x3fd)},
+ {NT_STATUS(0xc0000182), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000183), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000184), W_ERROR(0x16)},
+ {NT_STATUS(0xc0000185), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000186), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000188), W_ERROR(0x5de)},
+ {NT_STATUS(0xc0000189), W_ERROR(0x13)},
+ {NT_STATUS(0xc000018a), W_ERROR(0x6fa)},
+ {NT_STATUS(0xc000018b), W_ERROR(0x6fb)},
+ {NT_STATUS(0xc000018c), W_ERROR(0x6fc)},
+ {NT_STATUS(0xc000018d), W_ERROR(0x6fd)},
+ {NT_STATUS(0xc000018e), W_ERROR(0x5dc)},
+ {NT_STATUS(0xc000018f), W_ERROR(0x5dd)},
+ {NT_STATUS(0xc0000190), W_ERROR(0x6fe)},
+ {NT_STATUS(0xc0000192), W_ERROR(0x700)},
+ {NT_STATUS(0xc0000193), W_ERROR(0x701)},
+ {NT_STATUS(0xc0000194), W_ERROR(0x46b)},
+ {NT_STATUS(0xc0000195), W_ERROR(0x4c3)},
+ {NT_STATUS(0xc0000196), W_ERROR(0x4c4)},
+ {NT_STATUS(0xc0000197), W_ERROR(0x5df)},
+ {NT_STATUS(0xc0000198), W_ERROR(0x70f)},
+ {NT_STATUS(0xc0000199), W_ERROR(0x710)},
+ {NT_STATUS(0xc000019a), W_ERROR(0x711)},
+ {NT_STATUS(0xc000019b), W_ERROR(0x712)},
+ {NT_STATUS(0xc0000202), W_ERROR(0x572)},
+ {NT_STATUS(0xc0000203), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000204), W_ERROR(0x717)},
+ {NT_STATUS(0xc0000205), W_ERROR(0x46a)},
+ {NT_STATUS(0xc0000206), W_ERROR(0x6f8)},
+ {NT_STATUS(0xc0000207), W_ERROR(0x4be)},
+ {NT_STATUS(0xc0000208), W_ERROR(0x4be)},
+ {NT_STATUS(0xc0000209), W_ERROR(0x44)},
+ {NT_STATUS(0xc000020a), W_ERROR(0x34)},
+ {NT_STATUS(0xc000020b), W_ERROR(0x40)},
+ {NT_STATUS(0xc000020c), W_ERROR(0x40)},
+ {NT_STATUS(0xc000020d), W_ERROR(0x40)},
+ {NT_STATUS(0xc000020e), W_ERROR(0x44)},
+ {NT_STATUS(0xc000020f), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000210), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000211), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000212), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000213), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000214), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000215), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000216), W_ERROR(0x32)},
+ {NT_STATUS(0xc0000217), W_ERROR(0x32)},
+ {NT_STATUS(0xc000021c), W_ERROR(0x17e6)},
+ {NT_STATUS(0xc0000220), W_ERROR(0x46c)},
+ {NT_STATUS(0xc0000221), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000224), W_ERROR(0x773)},
+ {NT_STATUS(0xc0000225), W_ERROR(0x490)},
+ {NT_STATUS(0xc000022a), W_ERROR(0xc000022a)},
+ {NT_STATUS(0xc000022b), W_ERROR(0xc000022b)},
+ {NT_STATUS(0xc000022d), W_ERROR(0x4d5)},
+ {NT_STATUS(0xc0000230), W_ERROR(0x492)},
+ {NT_STATUS(0xc0000233), W_ERROR(0x774)},
+ {NT_STATUS(0xc0000234), W_ERROR(0x775)},
+ {NT_STATUS(0xc0000235), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000236), W_ERROR(0x4c9)},
+ {NT_STATUS(0xc0000237), W_ERROR(0x4ca)},
+ {NT_STATUS(0xc0000238), W_ERROR(0x4cb)},
+ {NT_STATUS(0xc0000239), W_ERROR(0x4cc)},
+ {NT_STATUS(0xc000023a), W_ERROR(0x4cd)},
+ {NT_STATUS(0xc000023b), W_ERROR(0x4ce)},
+ {NT_STATUS(0xc000023c), W_ERROR(0x4cf)},
+ {NT_STATUS(0xc000023d), W_ERROR(0x4d0)},
+ {NT_STATUS(0xc000023e), W_ERROR(0x4d1)},
+ {NT_STATUS(0xc000023f), W_ERROR(0x4d2)},
+ {NT_STATUS(0xc0000240), W_ERROR(0x4d3)},
+ {NT_STATUS(0xc0000241), W_ERROR(0x4d4)},
+ {NT_STATUS(0xc0000243), W_ERROR(0x4c8)},
+ {NT_STATUS(0xc0000246), W_ERROR(0x4d6)},
+ {NT_STATUS(0xc0000247), W_ERROR(0x4d7)},
+ {NT_STATUS(0xc0000248), W_ERROR(0x4d8)},
+ {NT_STATUS(0xc0000249), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000253), W_ERROR(0x54f)},
+ {NT_STATUS(0xc0000257), W_ERROR(0x4d0)},
+ {NT_STATUS(0xc0000259), W_ERROR(0x573)},
+ {NT_STATUS(0xc000025e), W_ERROR(0x422)},
+ {NT_STATUS(0xc0000262), W_ERROR(0xb6)},
+ {NT_STATUS(0xc0000263), W_ERROR(0x7f)},
+ {NT_STATUS(0xc0000264), W_ERROR(0x120)},
+ {NT_STATUS(0xc0000265), W_ERROR(0x476)},
+ {NT_STATUS(0xc0000267), W_ERROR(0x10fe)},
+ {NT_STATUS(0xc000026c), W_ERROR(0x7d1)},
+ {NT_STATUS(0xc000026d), W_ERROR(0x4b1)},
+ {NT_STATUS(0xc000026e), W_ERROR(0x15)},
+ {NT_STATUS(0xc0000272), W_ERROR(0x491)},
+ {NT_STATUS(0xc0000275), W_ERROR(0x1126)},
+ {NT_STATUS(0xc0000276), W_ERROR(0x1129)},
+ {NT_STATUS(0xc0000277), W_ERROR(0x112a)},
+ {NT_STATUS(0xc0000278), W_ERROR(0x1128)},
+ {NT_STATUS(0xc0000279), W_ERROR(0x780)},
+ {NT_STATUS(0xc0000280), W_ERROR(0x781)},
+ {NT_STATUS(0xc0000281), W_ERROR(0xa1)},
+ {NT_STATUS(0xc0000283), W_ERROR(0x488)},
+ {NT_STATUS(0xc0000284), W_ERROR(0x489)},
+ {NT_STATUS(0xc0000285), W_ERROR(0x48a)},
+ {NT_STATUS(0xc0000286), W_ERROR(0x48b)},
+ {NT_STATUS(0xc0000287), W_ERROR(0x48c)},
+ {NT_STATUS(0xc000028a), W_ERROR(0x5)},
+ {NT_STATUS(0xc000028b), W_ERROR(0x5)},
+ {NT_STATUS(0xc000028d), W_ERROR(0x5)},
+ {NT_STATUS(0xc000028e), W_ERROR(0x5)},
+ {NT_STATUS(0xc000028f), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000290), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000291), W_ERROR(0x1777)},
+ {NT_STATUS(0xc0000292), W_ERROR(0x1778)},
+ {NT_STATUS(0xc0000293), W_ERROR(0x1772)},
+ {NT_STATUS(0xc0000295), W_ERROR(0x1068)},
+ {NT_STATUS(0xc0000296), W_ERROR(0x1069)},
+ {NT_STATUS(0xc0000297), W_ERROR(0x106a)},
+ {NT_STATUS(0xc0000298), W_ERROR(0x106b)},
+ {NT_STATUS(0xc0000299), W_ERROR(0x201a)},
+ {NT_STATUS(0xc000029a), W_ERROR(0x201b)},
+ {NT_STATUS(0xc000029b), W_ERROR(0x201c)},
+ {NT_STATUS(0xc000029c), W_ERROR(0x1)},
+ {NT_STATUS(0xc000029d), W_ERROR(0x10ff)},
+ {NT_STATUS(0xc000029e), W_ERROR(0x1100)},
+ {NT_STATUS(0xc000029f), W_ERROR(0x494)},
+ {NT_STATUS(0xc00002a1), W_ERROR(0x200a)},
+ {NT_STATUS(0xc00002a2), W_ERROR(0x200b)},
+ {NT_STATUS(0xc00002a3), W_ERROR(0x200c)},
+ {NT_STATUS(0xc00002a4), W_ERROR(0x200d)},
+ {NT_STATUS(0xc00002a5), W_ERROR(0x200e)},
+ {NT_STATUS(0xc00002a6), W_ERROR(0x200f)},
+ {NT_STATUS(0xc00002a7), W_ERROR(0x2010)},
+ {NT_STATUS(0xc00002a8), W_ERROR(0x2011)},
+ {NT_STATUS(0xc00002a9), W_ERROR(0x2012)},
+ {NT_STATUS(0xc00002aa), W_ERROR(0x2013)},
+ {NT_STATUS(0xc00002ab), W_ERROR(0x2014)},
+ {NT_STATUS(0xc00002ac), W_ERROR(0x2015)},
+ {NT_STATUS(0xc00002ad), W_ERROR(0x2016)},
+ {NT_STATUS(0xc00002ae), W_ERROR(0x2017)},
+ {NT_STATUS(0xc00002af), W_ERROR(0x2018)},
+ {NT_STATUS(0xc00002b0), W_ERROR(0x2019)},
+ {NT_STATUS(0xc00002b1), W_ERROR(0x211e)},
+ {NT_STATUS(0xc00002b2), W_ERROR(0x1127)},
+ {NT_STATUS(0xc00002b6), W_ERROR(0x651)},
+ {NT_STATUS(0xc00002b7), W_ERROR(0x49a)},
+ {NT_STATUS(0xc00002b8), W_ERROR(0x49b)},
+ {NT_STATUS(0xc00002c1), W_ERROR(0x2024)},
+ {NT_STATUS(0xc00002c3), W_ERROR(0x575)},
+ {NT_STATUS(0xc00002c5), W_ERROR(0x3e6)},
+ {NT_STATUS(0xc00002c6), W_ERROR(0x1075)},
+ {NT_STATUS(0xc00002c7), W_ERROR(0x1076)},
+ {NT_STATUS(0xc00002ca), W_ERROR(0x10e8)},
+ {NT_STATUS(0xc00002cb), W_ERROR(0x2138)},
+ {NT_STATUS(0xc00002cc), W_ERROR(0x4e3)},
+ {NT_STATUS(0xc00002cd), W_ERROR(0x2139)},
+ {NT_STATUS(0xc00002cf), W_ERROR(0x49d)},
+ {NT_STATUS(0xc00002d0), W_ERROR(0x213a)},
+ {NT_STATUS(0xc00002d4), W_ERROR(0x2141)},
+ {NT_STATUS(0xc00002d5), W_ERROR(0x2142)},
+ {NT_STATUS(0xc00002d6), W_ERROR(0x2143)},
+ {NT_STATUS(0xc00002d7), W_ERROR(0x2144)},
+ {NT_STATUS(0xc00002d8), W_ERROR(0x2145)},
+ {NT_STATUS(0xc00002d9), W_ERROR(0x2146)},
+ {NT_STATUS(0xc00002da), W_ERROR(0x2147)},
+ {NT_STATUS(0xc00002db), W_ERROR(0x2148)},
+ {NT_STATUS(0xc00002dc), W_ERROR(0x2149)},
+ {NT_STATUS(0xc00002dd), W_ERROR(0x32)},
+ {NT_STATUS(0xc00002df), W_ERROR(0x2151)},
+ {NT_STATUS(0xc00002e0), W_ERROR(0x2152)},
+ {NT_STATUS(0xc00002e1), W_ERROR(0x2153)},
+ {NT_STATUS(0xc00002e2), W_ERROR(0x2154)},
+ {NT_STATUS(0xc00002e3), W_ERROR(0x215d)},
+ {NT_STATUS(0xc00002e4), W_ERROR(0x2163)},
+ {NT_STATUS(0xc00002e5), W_ERROR(0x2164)},
+ {NT_STATUS(0xc00002e6), W_ERROR(0x2165)},
+ {NT_STATUS(0xc00002e7), W_ERROR(0x216d)},
+ {NT_STATUS(0xc00002fe), W_ERROR(0x45b)},
+ {NT_STATUS(0xc00002ff), W_ERROR(0x4e7)},
+ {NT_STATUS(0xc0000300), W_ERROR(0x4e6)},
+ {NT_STATUS(0x80000001), W_ERROR(0x80000001)},
+ {NT_STATUS(0x80000002), W_ERROR(0x3e6)},
+ {NT_STATUS(0x80000003), W_ERROR(0x80000003)},
+ {NT_STATUS(0x80000004), W_ERROR(0x80000004)},
+ {NT_STATUS(0x80000005), W_ERROR(0xea)},
+ {NT_STATUS(0x80000006), W_ERROR(0x12)},
+ {NT_STATUS(0x8000000b), W_ERROR(0x56f)},
+ {NT_STATUS(0x8000000d), W_ERROR(0x12b)},
+ {NT_STATUS(0x8000000e), W_ERROR(0x1c)},
+ {NT_STATUS(0x8000000f), W_ERROR(0x15)},
+ {NT_STATUS(0x80000010), W_ERROR(0x15)},
+ {NT_STATUS(0x80000011), W_ERROR(0xaa)},
+ {NT_STATUS(0x80000012), W_ERROR(0x103)},
+ {NT_STATUS(0x80000013), W_ERROR(0xfe)},
+ {NT_STATUS(0x80000014), W_ERROR(0xff)},
+ {NT_STATUS(0x80000015), W_ERROR(0xff)},
+ {NT_STATUS(0x80000016), W_ERROR(0x456)},
+ {NT_STATUS(0x8000001a), W_ERROR(0x103)},
+ {NT_STATUS(0x8000001b), W_ERROR(0x44d)},
+ {NT_STATUS(0x8000001c), W_ERROR(0x456)},
+ {NT_STATUS(0x8000001d), W_ERROR(0x457)},
+ {NT_STATUS(0x8000001e), W_ERROR(0x44c)},
+ {NT_STATUS(0x8000001f), W_ERROR(0x44e)},
+ {NT_STATUS(0x80000021), W_ERROR(0x44f)},
+ {NT_STATUS(0x80000022), W_ERROR(0x450)},
+ {NT_STATUS(0x80000025), W_ERROR(0x962)},
+ {NT_STATUS(0x80000288), W_ERROR(0x48d)},
+ {NT_STATUS(0x80000289), W_ERROR(0x48e)},
+ {NT_STATUS_OK, WERR_OK}
+};
+
+bool ntstatus_check_dos_mapping = true;
+
+/*
+ check if a DOS encoded NTSTATUS code maps to the given NTSTATUS code
+*/
+bool ntstatus_dos_equal(NTSTATUS status1, NTSTATUS status2)
+{
+ /* when we negotiate nt status support, we don't want to consider
+ the mapping of dos codes, as we want to catch the cases where
+ a forced dos code is needed
+ */
+ if (ntstatus_check_dos_mapping) {
+ return NT_STATUS_V(status1) == NT_STATUS_V(status2);
+ }
+
+ /* otherwise check if the mapping comes out right. Note that it is important
+ that we do the mapping only from ntstatus -> dos and not from dos -> ntstatus,
+ as that is the mapping that servers must do */
+ if (!NT_STATUS_IS_DOS(status1) && NT_STATUS_IS_DOS(status2)) {
+ uint8_t eclass;
+ uint32_t ecode;
+ ntstatus_to_dos(status1, &eclass, &ecode);
+ return eclass == NT_STATUS_DOS_CLASS(status2) &&
+ ecode == NT_STATUS_DOS_CODE(status2);
+ }
+ if (NT_STATUS_IS_DOS(status1) && !NT_STATUS_IS_DOS(status2)) {
+ uint8_t eclass;
+ uint32_t ecode;
+ ntstatus_to_dos(status2, &eclass, &ecode);
+ return eclass == NT_STATUS_DOS_CLASS(status1) &&
+ ecode == NT_STATUS_DOS_CODE(status1);
+ }
+ return NT_STATUS_V(status1) == NT_STATUS_V(status2);
+}
+
+/*****************************************************************************
+convert a NT status code to a dos class/code
+ *****************************************************************************/
+void ntstatus_to_dos(NTSTATUS ntstatus, uint8_t *eclass, uint32_t *ecode)
+{
+ int i;
+ if (NT_STATUS_IS_OK(ntstatus)) {
+ *eclass = 0;
+ *ecode = 0;
+ return;
+ }
+ if (NT_STATUS_IS_DOS(ntstatus)) {
+ *eclass = NT_STATUS_DOS_CLASS(ntstatus);
+ *ecode = NT_STATUS_DOS_CODE(ntstatus);
+ return;
+ }
+ for (i=0; NT_STATUS_V(ntstatus_to_dos_map[i].ntstatus); i++) {
+ if (NT_STATUS_V(ntstatus) ==
+ NT_STATUS_V(ntstatus_to_dos_map[i].ntstatus)) {
+ *eclass = ntstatus_to_dos_map[i].dos_class;
+ *ecode = ntstatus_to_dos_map[i].dos_code;
+ return;
+ }
+ }
+ *eclass = ERRHRD;
+ *ecode = ERRgeneral;
+}
+
+
+/*****************************************************************************
+convert a WERROR to a NT status32 code
+ *****************************************************************************/
+NTSTATUS werror_to_ntstatus(WERROR error)
+{
+ int i;
+ if (W_ERROR_IS_OK(error)) return NT_STATUS_OK;
+ for (i=0; NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus); i++) {
+ if (W_ERROR_V(error) ==
+ W_ERROR_V(ntstatus_to_werror_map[i].werror)) {
+ return ntstatus_to_werror_map[i].ntstatus;
+ }
+ }
+
+ /* just guess ... */
+ return NT_STATUS(W_ERROR_V(error) | 0xc0000000);
+}
+
+/*****************************************************************************
+convert a NTSTATUS to a WERROR
+ *****************************************************************************/
+WERROR ntstatus_to_werror(NTSTATUS error)
+{
+ int i;
+ if (NT_STATUS_IS_OK(error)) return WERR_OK;
+ for (i=0; NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus); i++) {
+ if (NT_STATUS_V(error) ==
+ NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus)) {
+ return ntstatus_to_werror_map[i].werror;
+ }
+ }
+
+ /* a lame guess */
+ return W_ERROR(NT_STATUS_V(error) & 0xffff);
+}
+
+/* Mapping between Unix, DOS and NT error numbers */
+
+struct unix_error_map {
+ int unix_error;
+ NTSTATUS nt_error;
+};
+
+const struct unix_error_map unix_nt_errmap[] = {
+ { EAGAIN, STATUS_MORE_ENTRIES },
+ { EINTR, STATUS_MORE_ENTRIES },
+ { ENOBUFS, STATUS_MORE_ENTRIES },
+#ifdef EWOULDBLOCK
+ { EWOULDBLOCK, STATUS_MORE_ENTRIES },
+#endif
+ { EINPROGRESS, NT_STATUS_MORE_PROCESSING_REQUIRED },
+ { EPERM, NT_STATUS_ACCESS_DENIED },
+ { EACCES, NT_STATUS_ACCESS_DENIED },
+ { ENOENT, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { ENOTDIR, NT_STATUS_NOT_A_DIRECTORY },
+ { EIO, NT_STATUS_IO_DEVICE_ERROR },
+ { EBADF, NT_STATUS_INVALID_HANDLE },
+ { EINVAL, NT_STATUS_INVALID_PARAMETER },
+ { EEXIST, NT_STATUS_OBJECT_NAME_COLLISION},
+ { ENFILE, NT_STATUS_TOO_MANY_OPENED_FILES },
+ { EMFILE, NT_STATUS_TOO_MANY_OPENED_FILES },
+ { ENOSPC, NT_STATUS_DISK_FULL },
+ { EISDIR, NT_STATUS_FILE_IS_A_DIRECTORY },
+ { ENOTSOCK, NT_STATUS_INVALID_HANDLE },
+ { EFAULT, NT_STATUS_INVALID_PARAMETER },
+ { EMSGSIZE, NT_STATUS_INVALID_BUFFER_SIZE },
+ { ENOMEM, NT_STATUS_NO_MEMORY },
+ { EPIPE, NT_STATUS_CONNECTION_DISCONNECTED },
+ { ECONNREFUSED, NT_STATUS_CONNECTION_REFUSED },
+#ifdef ECONNRESET
+ { ECONNRESET, NT_STATUS_CONNECTION_RESET },
+#endif
+ { EBUSY, NT_STATUS_SHARING_VIOLATION },
+#ifdef ENOTSUP
+ { ENOTSUP, NT_STATUS_NOT_SUPPORTED},
+#endif
+#ifdef EOPNOTSUPP
+ { EOPNOTSUPP, NT_STATUS_NOT_SUPPORTED},
+#endif
+#ifdef EHOSTUNREACH
+ { EHOSTUNREACH, NT_STATUS_HOST_UNREACHABLE },
+#endif
+#ifdef ENETUNREACH
+ { ENETUNREACH, NT_STATUS_NETWORK_UNREACHABLE },
+#endif
+#ifdef ETIMEDOUT
+ { ETIMEDOUT, NT_STATUS_IO_TIMEOUT },
+#endif
+#ifdef EADDRINUSE
+ { EADDRINUSE, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED },
+#endif
+#ifdef ENOATTR
+ { ENOATTR, NT_STATUS_NOT_FOUND },
+#endif
+#ifdef ENODATA
+ { ENODATA, NT_STATUS_NOT_FOUND },
+#endif
+#ifdef EDQUOT
+ { EDQUOT, NT_STATUS_QUOTA_EXCEEDED },
+#endif
+#ifdef ENOTEMPTY
+ { ENOTEMPTY, NT_STATUS_DIRECTORY_NOT_EMPTY },
+#endif
+#ifdef EXDEV
+ { EXDEV, NT_STATUS_NOT_SAME_DEVICE },
+#endif
+#ifdef EROFS
+ { EROFS, NT_STATUS_MEDIA_WRITE_PROTECTED },
+#endif
+#ifdef ENAMETOOLONG
+ { ENAMETOOLONG, NT_STATUS_NAME_TOO_LONG },
+#endif
+#ifdef EFBIG
+ { EFBIG, NT_STATUS_DISK_FULL },
+#endif
+#ifdef EADDRNOTAVAIL
+ { EADDRNOTAVAIL,NT_STATUS_ADDRESS_NOT_ASSOCIATED },
+#endif
+#ifdef ESOCKTNOSUPPORT
+ { ESOCKTNOSUPPORT,NT_STATUS_INVALID_PARAMETER_MIX },
+#endif
+#ifdef EAFNOSUPPORT
+ { EAFNOSUPPORT, NT_STATUS_INVALID_PARAMETER_MIX },
+#endif
+#ifdef ENOPROTOOPT
+ { ENOPROTOOPT, NT_STATUS_INVALID_PARAMETER_MIX },
+#endif
+#ifdef ENODEV
+ { ENODEV, NT_STATUS_NO_SUCH_DEVICE },
+#endif
+#ifdef ENOSYS
+ { ENOSYS, NT_STATUS_INVALID_SYSTEM_SERVICE },
+#endif
+#ifdef ECANCELED
+ { ECANCELED, NT_STATUS_CANCELLED },
+#endif
+
+ { 0, NT_STATUS_UNSUCCESSFUL }
+};
+
+
+/*********************************************************************
+ Map an NT error code from a Unix error code.
+*********************************************************************/
+NTSTATUS map_nt_error_from_unix(int unix_error)
+{
+ int i;
+
+ /* Look through list */
+ for (i=0;i<ARRAY_SIZE(unix_nt_errmap);i++) {
+ if (unix_nt_errmap[i].unix_error == unix_error) {
+ return unix_nt_errmap[i].nt_error;
+ }
+ }
+
+ /* Default return */
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS ndr_map_error2ntstatus(enum ndr_err_code ndr_err)
+{
+ switch (ndr_err) {
+ case NDR_ERR_SUCCESS:
+ return NT_STATUS_OK;
+ case NDR_ERR_BUFSIZE:
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ case NDR_ERR_TOKEN:
+ return NT_STATUS_INTERNAL_ERROR;
+ case NDR_ERR_ALLOC:
+ return NT_STATUS_NO_MEMORY;
+ case NDR_ERR_ARRAY_SIZE:
+ return NT_STATUS_ARRAY_BOUNDS_EXCEEDED;
+ case NDR_ERR_INVALID_POINTER:
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ case NDR_ERR_UNREAD_BYTES:
+ return NT_STATUS_PORT_MESSAGE_TOO_LONG;
+ default:
+ break;
+ }
+
+ /* we should map all error codes to different status codes */
+ return NT_STATUS_INVALID_PARAMETER;
+}
diff --git a/source4/libcli/util/nterr.c b/source4/libcli/util/nterr.c
new file mode 100644
index 0000000000..4e7cdf5c3a
--- /dev/null
+++ b/source4/libcli/util/nterr.c
@@ -0,0 +1,898 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Luke Kenneth Casson Leighton 1997-2001.
+ *
+ * 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/>.
+ */
+
+/* NT error codes. please read nterr.h */
+
+#include "includes.h"
+#include "../libcli/ldap/ldap_errors.h"
+#undef strcasecmp
+
+typedef struct
+{
+ const char *nt_errstr;
+ NTSTATUS nt_errcode;
+} nt_err_code_struct;
+
+#define DOS_CODE(class, code) { #class ":" #code, NT_STATUS_DOS(class, code) }
+#define LDAP_CODE(code) { #code, NT_STATUS_LDAP(code) }
+
+static const nt_err_code_struct nt_errs[] =
+{
+ { "NT_STATUS_OK", NT_STATUS_OK },
+ { "STATUS_NO_MORE_FILES", STATUS_NO_MORE_FILES },
+ { "STATUS_NO_MORE_EAS", STATUS_NO_MORE_EAS },
+ { "STATUS_INVALID_EA_NAME", STATUS_INVALID_EA_NAME },
+ { "STATUS_EA_LIST_INCONSISTENT", STATUS_EA_LIST_INCONSISTENT },
+ { "STATUS_INVALID_EA_FLAG", STATUS_INVALID_EA_FLAG },
+ { "NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL },
+ { "NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED },
+ { "NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS },
+ { "NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH },
+ { "NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION },
+ { "STATUS_BUFFER_OVERFLOW", STATUS_BUFFER_OVERFLOW },
+ { "NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR },
+ { "NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA },
+ { "NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE },
+ { "NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK },
+ { "NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC },
+ { "NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID },
+ { "NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED },
+ { "NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER },
+ { "NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE },
+ { "NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE },
+ { "NT_STATUS_INVALID_DEVICE_REQUEST", NT_STATUS_INVALID_DEVICE_REQUEST },
+ { "NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE },
+ { "NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME },
+ { "NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE },
+ { "NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA },
+ { "NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR },
+ { "NT_STATUS_MORE_PROCESSING_REQUIRED", NT_STATUS_MORE_PROCESSING_REQUIRED },
+ { "NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY },
+ { "NT_STATUS_CONFLICTING_ADDRESSES", NT_STATUS_CONFLICTING_ADDRESSES },
+ { "NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW },
+ { "NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM },
+ { "NT_STATUS_UNABLE_TO_DELETE_SECTION", NT_STATUS_UNABLE_TO_DELETE_SECTION },
+ { "NT_STATUS_INVALID_SYSTEM_SERVICE", NT_STATUS_INVALID_SYSTEM_SERVICE },
+ { "NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION },
+ { "NT_STATUS_INVALID_LOCK_SEQUENCE", NT_STATUS_INVALID_LOCK_SEQUENCE },
+ { "NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE },
+ { "NT_STATUS_INVALID_FILE_FOR_SECTION", NT_STATUS_INVALID_FILE_FOR_SECTION },
+ { "NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED },
+ { "NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED },
+ { "NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL },
+ { "NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH },
+ { "NT_STATUS_NONCONTINUABLE_EXCEPTION", NT_STATUS_NONCONTINUABLE_EXCEPTION },
+ { "NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION },
+ { "NT_STATUS_UNWIND", NT_STATUS_UNWIND },
+ { "NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK },
+ { "NT_STATUS_INVALID_UNWIND_TARGET", NT_STATUS_INVALID_UNWIND_TARGET },
+ { "NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED },
+ { "NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR },
+ { "NT_STATUS_UNABLE_TO_DECOMMIT_VM", NT_STATUS_UNABLE_TO_DECOMMIT_VM },
+ { "NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED },
+ { "NT_STATUS_INVALID_PORT_ATTRIBUTES", NT_STATUS_INVALID_PORT_ATTRIBUTES },
+ { "NT_STATUS_PORT_MESSAGE_TOO_LONG", NT_STATUS_PORT_MESSAGE_TOO_LONG },
+ { "NT_STATUS_INVALID_PARAMETER_MIX", NT_STATUS_INVALID_PARAMETER_MIX },
+ { "NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER },
+ { "NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR },
+ { "NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID },
+ { "NT_STATUS_OBJECT_NAME_NOT_FOUND", NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { "NT_STATUS_OBJECT_NAME_COLLISION", NT_STATUS_OBJECT_NAME_COLLISION },
+ { "NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE },
+ { "NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED },
+ { "NT_STATUS_DEVICE_ALREADY_ATTACHED", NT_STATUS_DEVICE_ALREADY_ATTACHED },
+ { "NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID },
+ { "NT_STATUS_OBJECT_PATH_NOT_FOUND", NT_STATUS_OBJECT_PATH_NOT_FOUND },
+ { "NT_STATUS_OBJECT_PATH_SYNTAX_BAD", NT_STATUS_OBJECT_PATH_SYNTAX_BAD },
+ { "NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN },
+ { "NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR },
+ { "NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR },
+ { "NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR },
+ { "NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG },
+ { "NT_STATUS_PORT_CONNECTION_REFUSED", NT_STATUS_PORT_CONNECTION_REFUSED },
+ { "NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE },
+ { "NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION },
+ { "NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED },
+ { "NT_STATUS_INVALID_PAGE_PROTECTION", NT_STATUS_INVALID_PAGE_PROTECTION },
+ { "NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED },
+ { "NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED", NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED },
+ { "NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET },
+ { "NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE },
+ { "NT_STATUS_SUSPEND_COUNT_EXCEEDED", NT_STATUS_SUSPEND_COUNT_EXCEEDED },
+ { "NT_STATUS_THREAD_IS_TERMINATING", NT_STATUS_THREAD_IS_TERMINATING },
+ { "NT_STATUS_BAD_WORKING_SET_LIMIT", NT_STATUS_BAD_WORKING_SET_LIMIT },
+ { "NT_STATUS_INCOMPATIBLE_FILE_MAP", NT_STATUS_INCOMPATIBLE_FILE_MAP },
+ { "NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION },
+ { "NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED },
+ { "NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE },
+ { "NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY },
+ { "NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE },
+ { "NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR },
+ { "NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT },
+ { "NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED },
+ { "NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING },
+ { "NT_STATUS_CTL_FILE_NOT_SUPPORTED", NT_STATUS_CTL_FILE_NOT_SUPPORTED },
+ { "NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION },
+ { "NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH },
+ { "NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER },
+ { "NT_STATUS_INVALID_PRIMARY_GROUP", NT_STATUS_INVALID_PRIMARY_GROUP },
+ { "NT_STATUS_NO_IMPERSONATION_TOKEN", NT_STATUS_NO_IMPERSONATION_TOKEN },
+ { "NT_STATUS_CANT_DISABLE_MANDATORY", NT_STATUS_CANT_DISABLE_MANDATORY },
+ { "NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS },
+ { "NT_STATUS_NO_SUCH_LOGON_SESSION", NT_STATUS_NO_SUCH_LOGON_SESSION },
+ { "NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE },
+ { "NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD },
+ { "NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME },
+ { "NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS },
+ { "NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER },
+ { "NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS },
+ { "NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP },
+ { "NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP },
+ { "NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP },
+ { "NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN },
+ { "NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD },
+ { "NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD },
+ { "NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION },
+ { "NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE },
+ { "NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION },
+ { "NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS },
+ { "NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION },
+ { "NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED },
+ { "NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED },
+ { "NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED },
+ { "NT_STATUS_TOO_MANY_LUIDS_REQUESTED", NT_STATUS_TOO_MANY_LUIDS_REQUESTED },
+ { "NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED },
+ { "NT_STATUS_INVALID_SUB_AUTHORITY", NT_STATUS_INVALID_SUB_AUTHORITY },
+ { "NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL },
+ { "NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID },
+ { "NT_STATUS_INVALID_SECURITY_DESCR", NT_STATUS_INVALID_SECURITY_DESCR },
+ { "NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND },
+ { "NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT },
+ { "NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN },
+ { "NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL },
+ { "NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED },
+ { "NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL },
+ { "NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED },
+ { "NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED },
+ { "NT_STATUS_TOO_MANY_GUIDS_REQUESTED", NT_STATUS_TOO_MANY_GUIDS_REQUESTED },
+ { "NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED },
+ { "NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY },
+ { "NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED },
+ { "NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL },
+ { "NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED },
+ { "NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA },
+ { "NT_STATUS_RESOURCE_DATA_NOT_FOUND", NT_STATUS_RESOURCE_DATA_NOT_FOUND },
+ { "NT_STATUS_RESOURCE_TYPE_NOT_FOUND", NT_STATUS_RESOURCE_TYPE_NOT_FOUND },
+ { "NT_STATUS_RESOURCE_NAME_NOT_FOUND", NT_STATUS_RESOURCE_NAME_NOT_FOUND },
+ { "NT_STATUS_ARRAY_BOUNDS_EXCEEDED", NT_STATUS_ARRAY_BOUNDS_EXCEEDED },
+ { "NT_STATUS_FLOAT_DENORMAL_OPERAND", NT_STATUS_FLOAT_DENORMAL_OPERAND },
+ { "NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO },
+ { "NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT },
+ { "NT_STATUS_FLOAT_INVALID_OPERATION", NT_STATUS_FLOAT_INVALID_OPERATION },
+ { "NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW },
+ { "NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK },
+ { "NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW },
+ { "NT_STATUS_INTEGER_DIVIDE_BY_ZERO", NT_STATUS_INTEGER_DIVIDE_BY_ZERO },
+ { "NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW },
+ { "NT_STATUS_PRIVILEGED_INSTRUCTION", NT_STATUS_PRIVILEGED_INSTRUCTION },
+ { "NT_STATUS_TOO_MANY_PAGING_FILES", NT_STATUS_TOO_MANY_PAGING_FILES },
+ { "NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID },
+ { "NT_STATUS_ALLOTTED_SPACE_EXCEEDED", NT_STATUS_ALLOTTED_SPACE_EXCEEDED },
+ { "NT_STATUS_INSUFFICIENT_RESOURCES", NT_STATUS_INSUFFICIENT_RESOURCES },
+ { "NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND },
+ { "NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR },
+ { "NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED },
+ { "NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE },
+ { "NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE },
+ { "NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED },
+ { "NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA },
+ { "NT_STATUS_MEDIA_WRITE_PROTECTED", NT_STATUS_MEDIA_WRITE_PROTECTED },
+ { "NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY },
+ { "NT_STATUS_INVALID_GROUP_ATTRIBUTES", NT_STATUS_INVALID_GROUP_ATTRIBUTES },
+ { "NT_STATUS_BAD_IMPERSONATION_LEVEL", NT_STATUS_BAD_IMPERSONATION_LEVEL },
+ { "NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS },
+ { "NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS },
+ { "NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE },
+ { "NT_STATUS_BAD_MASTER_BOOT_RECORD", NT_STATUS_BAD_MASTER_BOOT_RECORD },
+ { "NT_STATUS_INSTRUCTION_MISALIGNMENT", NT_STATUS_INSTRUCTION_MISALIGNMENT },
+ { "NT_STATUS_INSTANCE_NOT_AVAILABLE", NT_STATUS_INSTANCE_NOT_AVAILABLE },
+ { "NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE },
+ { "NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE },
+ { "NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY },
+ { "NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION },
+ { "NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED },
+ { "NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING },
+ { "NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED },
+ { "NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING },
+ { "NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE },
+ { "NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT },
+ { "NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED },
+ { "NT_STATUS_PROFILING_NOT_STARTED", NT_STATUS_PROFILING_NOT_STARTED },
+ { "NT_STATUS_PROFILING_NOT_STOPPED", NT_STATUS_PROFILING_NOT_STOPPED },
+ { "NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET },
+ { "NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY },
+ { "NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED },
+ { "NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING },
+ { "NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME },
+ { "NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH },
+ { "NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY },
+ { "NT_STATUS_DEVICE_DOES_NOT_EXIST", NT_STATUS_DEVICE_DOES_NOT_EXIST },
+ { "NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS },
+ { "NT_STATUS_ADAPTER_HARDWARE_ERROR", NT_STATUS_ADAPTER_HARDWARE_ERROR },
+ { "NT_STATUS_INVALID_NETWORK_RESPONSE", NT_STATUS_INVALID_NETWORK_RESPONSE },
+ { "NT_STATUS_UNEXPECTED_NETWORK_ERROR", NT_STATUS_UNEXPECTED_NETWORK_ERROR },
+ { "NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER },
+ { "NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL },
+ { "NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE },
+ { "NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED },
+ { "NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED },
+ { "NT_STATUS_NETWORK_ACCESS_DENIED", NT_STATUS_NETWORK_ACCESS_DENIED },
+ { "NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE },
+ { "NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME },
+ { "NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES },
+ { "NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS },
+ { "NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED },
+ { "NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED },
+ { "NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED },
+ { "NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT },
+ { "NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT },
+ { "NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE },
+ { "NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED },
+ { "NT_STATUS_VIRTUAL_CIRCUIT_CLOSED", NT_STATUS_VIRTUAL_CIRCUIT_CLOSED },
+ { "NT_STATUS_NO_SECURITY_ON_OBJECT", NT_STATUS_NO_SECURITY_ON_OBJECT },
+ { "NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT },
+ { "NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY },
+ { "NT_STATUS_CANT_ACCESS_DOMAIN_INFO", NT_STATUS_CANT_ACCESS_DOMAIN_INFO },
+ { "NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF },
+ { "NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE },
+ { "NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE },
+ { "NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE },
+ { "NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN },
+ { "NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS },
+ { "NT_STATUS_DOMAIN_LIMIT_EXCEEDED", NT_STATUS_DOMAIN_LIMIT_EXCEEDED },
+ { "NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED },
+ { "NT_STATUS_INVALID_OPLOCK_PROTOCOL", NT_STATUS_INVALID_OPLOCK_PROTOCOL },
+ { "NT_STATUS_INTERNAL_DB_CORRUPTION", NT_STATUS_INTERNAL_DB_CORRUPTION },
+ { "NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR },
+ { "NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED },
+ { "NT_STATUS_BAD_DESCRIPTOR_FORMAT", NT_STATUS_BAD_DESCRIPTOR_FORMAT },
+ { "NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER },
+ { "NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR },
+ { "NT_STATUS_UNEXPECTED_MM_CREATE_ERR", NT_STATUS_UNEXPECTED_MM_CREATE_ERR },
+ { "NT_STATUS_UNEXPECTED_MM_MAP_ERROR", NT_STATUS_UNEXPECTED_MM_MAP_ERROR },
+ { "NT_STATUS_UNEXPECTED_MM_EXTEND_ERR", NT_STATUS_UNEXPECTED_MM_EXTEND_ERR },
+ { "NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS },
+ { "NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS },
+ { "NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1 },
+ { "NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2 },
+ { "NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3 },
+ { "NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4 },
+ { "NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5 },
+ { "NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6 },
+ { "NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7 },
+ { "NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8 },
+ { "NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9 },
+ { "NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10 },
+ { "NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11 },
+ { "NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12 },
+ { "NT_STATUS_REDIRECTOR_NOT_STARTED", NT_STATUS_REDIRECTOR_NOT_STARTED },
+ { "NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED },
+ { "NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW },
+ { "NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE },
+ { "NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE },
+ { "NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY },
+ { "NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR },
+ { "NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY },
+ { "NT_STATUS_BAD_LOGON_SESSION_STATE", NT_STATUS_BAD_LOGON_SESSION_STATE },
+ { "NT_STATUS_LOGON_SESSION_COLLISION", NT_STATUS_LOGON_SESSION_COLLISION },
+ { "NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG },
+ { "NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN },
+ { "NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE },
+ { "NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND },
+ { "NT_STATUS_PROCESS_IS_TERMINATING", NT_STATUS_PROCESS_IS_TERMINATING },
+ { "NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE },
+ { "NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION },
+ { "NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE },
+ { "NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED },
+ { "NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT },
+ { "NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST },
+ { "NT_STATUS_ABIOS_LID_ALREADY_OWNED", NT_STATUS_ABIOS_LID_ALREADY_OWNED },
+ { "NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER },
+ { "NT_STATUS_ABIOS_INVALID_COMMAND", NT_STATUS_ABIOS_INVALID_COMMAND },
+ { "NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID },
+ { "NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE", NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE },
+ { "NT_STATUS_ABIOS_INVALID_SELECTOR", NT_STATUS_ABIOS_INVALID_SELECTOR },
+ { "NT_STATUS_NO_LDT", NT_STATUS_NO_LDT },
+ { "NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE },
+ { "NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET },
+ { "NT_STATUS_INVALID_LDT_DESCRIPTOR", NT_STATUS_INVALID_LDT_DESCRIPTOR },
+ { "NT_STATUS_INVALID_IMAGE_NE_FORMAT", NT_STATUS_INVALID_IMAGE_NE_FORMAT },
+ { "NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE },
+ { "NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE },
+ { "NT_STATUS_MAPPED_FILE_SIZE_ZERO", NT_STATUS_MAPPED_FILE_SIZE_ZERO },
+ { "NT_STATUS_TOO_MANY_OPENED_FILES", NT_STATUS_TOO_MANY_OPENED_FILES },
+ { "NT_STATUS_CANCELLED", NT_STATUS_CANCELLED },
+ { "NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE },
+ { "NT_STATUS_INVALID_COMPUTER_NAME", NT_STATUS_INVALID_COMPUTER_NAME },
+ { "NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED },
+ { "NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT },
+ { "NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP },
+ { "NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER },
+ { "NT_STATUS_MEMBERS_PRIMARY_GROUP", NT_STATUS_MEMBERS_PRIMARY_GROUP },
+ { "NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED },
+ { "NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS },
+ { "NT_STATUS_THREAD_NOT_IN_PROCESS", NT_STATUS_THREAD_NOT_IN_PROCESS },
+ { "NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE },
+ { "NT_STATUS_PAGEFILE_QUOTA_EXCEEDED", NT_STATUS_PAGEFILE_QUOTA_EXCEEDED },
+ { "NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT },
+ { "NT_STATUS_INVALID_IMAGE_LE_FORMAT", NT_STATUS_INVALID_IMAGE_LE_FORMAT },
+ { "NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ },
+ { "NT_STATUS_INVALID_IMAGE_PROTECT", NT_STATUS_INVALID_IMAGE_PROTECT },
+ { "NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16 },
+ { "NT_STATUS_LOGON_SERVER_CONFLICT", NT_STATUS_LOGON_SERVER_CONFLICT },
+ { "NT_STATUS_TIME_DIFFERENCE_AT_DC", NT_STATUS_TIME_DIFFERENCE_AT_DC },
+ { "NT_STATUS_SYNCHRONIZATION_REQUIRED", NT_STATUS_SYNCHRONIZATION_REQUIRED },
+ { "NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND },
+ { "NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED },
+ { "NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED },
+ { "NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND },
+ { "NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND },
+ { "NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT },
+ { "NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT },
+ { "NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT },
+ { "NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES },
+ { "NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED },
+ { "NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT },
+ { "NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION },
+ { "NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS },
+ { "NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED },
+ { "NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE },
+ { "NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION },
+ { "NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE },
+ { "NT_STATUS_PAGEFILE_CREATE_FAILED", NT_STATUS_PAGEFILE_CREATE_FAILED },
+ { "NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE },
+ { "NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL },
+ { "NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE },
+ { "NT_STATUS_ILLEGAL_FLOAT_CONTEXT", NT_STATUS_ILLEGAL_FLOAT_CONTEXT },
+ { "NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN },
+ { "NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT },
+ { "NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED },
+ { "NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR },
+ { "NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME },
+ { "NT_STATUS_SERIAL_NO_DEVICE_INITED", NT_STATUS_SERIAL_NO_DEVICE_INITED },
+ { "NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS },
+ { "NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS },
+ { "NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS },
+ { "NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS },
+ { "NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED },
+ { "NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS },
+ { "NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG },
+ { "NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR },
+ { "NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE },
+ { "NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS },
+ { "NT_STATUS_LOGON_TYPE_NOT_GRANTED", NT_STATUS_LOGON_TYPE_NOT_GRANTED },
+ { "NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE },
+ { "NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED", NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED },
+ { "NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR", NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR },
+ { "NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER },
+ { "NT_STATUS_ILL_FORMED_SERVICE_ENTRY", NT_STATUS_ILL_FORMED_SERVICE_ENTRY },
+ { "NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER },
+ { "NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER },
+ { "NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER },
+ { "NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME },
+ { "NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND", NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND },
+ { "NT_STATUS_FLOPPY_WRONG_CYLINDER", NT_STATUS_FLOPPY_WRONG_CYLINDER },
+ { "NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR },
+ { "NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS },
+ { "NT_STATUS_DISK_RECALIBRATE_FAILED", NT_STATUS_DISK_RECALIBRATE_FAILED },
+ { "NT_STATUS_DISK_OPERATION_FAILED", NT_STATUS_DISK_OPERATION_FAILED },
+ { "NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED },
+ { "NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY },
+ { "NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING },
+ { "NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE },
+ { "NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH },
+ { "NT_STATUS_DEVICE_NOT_PARTITIONED", NT_STATUS_DEVICE_NOT_PARTITIONED },
+ { "NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA },
+ { "NT_STATUS_UNABLE_TO_UNLOAD_MEDIA", NT_STATUS_UNABLE_TO_UNLOAD_MEDIA },
+ { "NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW },
+ { "NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA },
+ { "NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER },
+ { "NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER },
+ { "NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED },
+ { "NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE },
+ { "NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS },
+ { "NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED", NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED },
+ { "NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN },
+ { "NT_STATUS_CHILD_MUST_BE_VOLATILE", NT_STATUS_CHILD_MUST_BE_VOLATILE },
+ { "NT_STATUS_DEVICE_CONFIGURATION_ERROR", NT_STATUS_DEVICE_CONFIGURATION_ERROR },
+ { "NT_STATUS_DRIVER_INTERNAL_ERROR", NT_STATUS_DRIVER_INTERNAL_ERROR },
+ { "NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE },
+ { "NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR },
+ { "NT_STATUS_DEVICE_PROTOCOL_ERROR", NT_STATUS_DEVICE_PROTOCOL_ERROR },
+ { "NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER },
+ { "NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL },
+ { "NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE },
+ { "NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET },
+ { "NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT },
+ { "NT_STATUS_TRUSTED_DOMAIN_FAILURE", NT_STATUS_TRUSTED_DOMAIN_FAILURE },
+ { "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE", NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE },
+ { "NT_STATUS_EVENTLOG_FILE_CORRUPT", NT_STATUS_EVENTLOG_FILE_CORRUPT },
+ { "NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START },
+ { "NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE },
+ { "NT_STATUS_MUTANT_LIMIT_EXCEEDED", NT_STATUS_MUTANT_LIMIT_EXCEEDED },
+ { "NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED },
+ { "NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED },
+ { "NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK },
+ { "NT_STATUS_NETWORK_CREDENTIAL_CONFLICT", NT_STATUS_NETWORK_CREDENTIAL_CONFLICT },
+ { "NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT },
+ { "NT_STATUS_EVENTLOG_FILE_CHANGED", NT_STATUS_EVENTLOG_FILE_CHANGED },
+ { "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT },
+ { "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT },
+ { "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT },
+ { "NT_STATUS_DOMAIN_TRUST_INCONSISTENT", NT_STATUS_DOMAIN_TRUST_INCONSISTENT },
+ { "NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED },
+ { "NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY },
+ { "NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED },
+ { "NT_STATUS_RESOURCE_LANG_NOT_FOUND", NT_STATUS_RESOURCE_LANG_NOT_FOUND },
+ { "NT_STATUS_INSUFF_SERVER_RESOURCES", NT_STATUS_INSUFF_SERVER_RESOURCES },
+ { "NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE },
+ { "NT_STATUS_INVALID_ADDRESS_COMPONENT", NT_STATUS_INVALID_ADDRESS_COMPONENT },
+ { "NT_STATUS_INVALID_ADDRESS_WILDCARD", NT_STATUS_INVALID_ADDRESS_WILDCARD },
+ { "NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES },
+ { "NT_STATUS_ADDRESS_ALREADY_EXISTS", NT_STATUS_ADDRESS_ALREADY_EXISTS },
+ { "NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED },
+ { "NT_STATUS_CONNECTION_DISCONNECTED", NT_STATUS_CONNECTION_DISCONNECTED },
+ { "NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET },
+ { "NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES },
+ { "NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED },
+ { "NT_STATUS_TRANSACTION_TIMED_OUT", NT_STATUS_TRANSACTION_TIMED_OUT },
+ { "NT_STATUS_TRANSACTION_NO_RELEASE", NT_STATUS_TRANSACTION_NO_RELEASE },
+ { "NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH },
+ { "NT_STATUS_TRANSACTION_RESPONDED", NT_STATUS_TRANSACTION_RESPONDED },
+ { "NT_STATUS_TRANSACTION_INVALID_ID", NT_STATUS_TRANSACTION_INVALID_ID },
+ { "NT_STATUS_TRANSACTION_INVALID_TYPE", NT_STATUS_TRANSACTION_INVALID_TYPE },
+ { "NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION },
+ { "NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION },
+ { "NT_STATUS_CANNOT_LOAD_REGISTRY_FILE", NT_STATUS_CANNOT_LOAD_REGISTRY_FILE },
+ { "NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED },
+ { "NT_STATUS_SYSTEM_PROCESS_TERMINATED", NT_STATUS_SYSTEM_PROCESS_TERMINATED },
+ { "NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED },
+ { "NT_STATUS_NO_BROWSER_SERVERS_FOUND", NT_STATUS_NO_BROWSER_SERVERS_FOUND },
+ { "NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR },
+ { "NT_STATUS_DRIVER_CANCEL_TIMEOUT", NT_STATUS_DRIVER_CANCEL_TIMEOUT },
+ { "NT_STATUS_REPLY_MESSAGE_MISMATCH", NT_STATUS_REPLY_MESSAGE_MISMATCH },
+ { "NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT },
+ { "NT_STATUS_IMAGE_CHECKSUM_MISMATCH", NT_STATUS_IMAGE_CHECKSUM_MISMATCH },
+ { "NT_STATUS_LOST_WRITEBEHIND_DATA", NT_STATUS_LOST_WRITEBEHIND_DATA },
+ { "NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID", NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID },
+ { "NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE },
+ { "NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND },
+ { "NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM },
+ { "NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE },
+ { "NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ },
+ { "NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK },
+ { "NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID },
+ { "NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS },
+ { "NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE },
+ { "NT_STATUS_RETRY", NT_STATUS_RETRY },
+ { "NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE },
+ { "NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET },
+ { "NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND },
+ { "NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW },
+ { "NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT },
+ { "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND },
+ { "NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT },
+ { "NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE },
+ { "NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED },
+ { "NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT },
+ { "NT_STATUS_ADDRESS_ALREADY_ASSOCIATED", NT_STATUS_ADDRESS_ALREADY_ASSOCIATED },
+ { "NT_STATUS_ADDRESS_NOT_ASSOCIATED", NT_STATUS_ADDRESS_NOT_ASSOCIATED },
+ { "NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID },
+ { "NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE },
+ { "NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE },
+ { "NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE },
+ { "NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE },
+ { "NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE },
+ { "NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED },
+ { "NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED },
+ { "NT_STATUS_BAD_COMPRESSION_BUFFER", NT_STATUS_BAD_COMPRESSION_BUFFER },
+ { "NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE },
+ { "NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED },
+ { "NT_STATUS_TIMER_RESOLUTION_NOT_SET", NT_STATUS_TIMER_RESOLUTION_NOT_SET },
+ { "NT_STATUS_CONNECTION_COUNT_LIMIT", NT_STATUS_CONNECTION_COUNT_LIMIT },
+ { "NT_STATUS_LOGIN_TIME_RESTRICTION", NT_STATUS_LOGIN_TIME_RESTRICTION },
+ { "NT_STATUS_LOGIN_WKSTA_RESTRICTION", NT_STATUS_LOGIN_WKSTA_RESTRICTION },
+ { "NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH },
+ { "NT_STATUS_INSUFFICIENT_LOGON_INFO", NT_STATUS_INSUFFICIENT_LOGON_INFO },
+ { "NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT },
+ { "NT_STATUS_BAD_SERVICE_ENTRYPOINT", NT_STATUS_BAD_SERVICE_ENTRYPOINT },
+ { "NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST },
+ { "NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1 },
+ { "NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2 },
+ { "NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT },
+ { "NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED },
+ { "NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE },
+ { "NT_STATUS_LICENSE_QUOTA_EXCEEDED", NT_STATUS_LICENSE_QUOTA_EXCEEDED },
+ { "NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT },
+ { "NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT },
+ { "NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT },
+ { "NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE },
+ { "NT_STATUS_UNSUPPORTED_COMPRESSION", NT_STATUS_UNSUPPORTED_COMPRESSION },
+ { "NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE },
+ { "NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH", NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH },
+ { "NT_STATUS_DRIVER_ORDINAL_NOT_FOUND", NT_STATUS_DRIVER_ORDINAL_NOT_FOUND },
+ { "NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND", NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND },
+ { "NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED },
+ { "NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS },
+ { "NT_STATUS_QUOTA_LIST_INCONSISTENT", NT_STATUS_QUOTA_LIST_INCONSISTENT },
+ { "NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE },
+ { "NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES },
+ { "NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED", NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED },
+ { "NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX", NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX },
+ { "NT_STATUS_CURRENT_DOMAIN_NOT_ALLOWED", NT_STATUS_CURRENT_DOMAIN_NOT_ALLOWED },
+ { "NT_STATUS_OBJECTID_NOT_FOUND", NT_STATUS_OBJECTID_NOT_FOUND },
+ { "NT_STATUS_DOWNGRADE_DETECTED", NT_STATUS_DOWNGRADE_DETECTED },
+ { "NT_STATUS_DS_BUSY", NT_STATUS_DS_BUSY },
+ { "STATUS_MORE_ENTRIES", STATUS_MORE_ENTRIES },
+ { "STATUS_SOME_UNMAPPED", STATUS_SOME_UNMAPPED },
+ { "STATUS_NOTIFY_CLEANUP", STATUS_NOTIFY_CLEANUP },
+ { "STATUS_NOTIFY_ENUM_DIR", STATUS_NOTIFY_ENUM_DIR },
+
+ DOS_CODE(ERRDOS, ERRsuccess),
+ DOS_CODE(ERRDOS, ERRbadfunc),
+ DOS_CODE(ERRDOS, ERRbadfile),
+ DOS_CODE(ERRDOS, ERRbadpath),
+ DOS_CODE(ERRDOS, ERRnofids),
+ DOS_CODE(ERRDOS, ERRnoaccess),
+ DOS_CODE(ERRDOS, ERRbadfid),
+ DOS_CODE(ERRDOS, ERRbadmcb),
+ DOS_CODE(ERRDOS, ERRnomem),
+ DOS_CODE(ERRDOS, ERRbadmem),
+ DOS_CODE(ERRDOS, ERRbadenv),
+ DOS_CODE(ERRDOS, ERRbadaccess),
+ DOS_CODE(ERRDOS, ERRbaddata),
+ DOS_CODE(ERRDOS, ERRres),
+ DOS_CODE(ERRDOS, ERRbaddrive),
+ DOS_CODE(ERRDOS, ERRremcd),
+ DOS_CODE(ERRDOS, ERRdiffdevice),
+ DOS_CODE(ERRDOS, ERRnofiles),
+ DOS_CODE(ERRDOS, ERRgeneral),
+ DOS_CODE(ERRDOS, ERRbadshare),
+ DOS_CODE(ERRDOS, ERRlock),
+ DOS_CODE(ERRDOS, ERRunsup),
+ DOS_CODE(ERRDOS, ERRnetnamedel),
+ DOS_CODE(ERRDOS, ERRnosuchshare),
+ DOS_CODE(ERRDOS, ERRfilexists),
+ DOS_CODE(ERRDOS, ERRinvalidparam),
+ DOS_CODE(ERRDOS, ERRcannotopen),
+ DOS_CODE(ERRDOS, ERRinsufficientbuffer),
+ DOS_CODE(ERRDOS, ERRinvalidname),
+ DOS_CODE(ERRDOS, ERRunknownlevel),
+ DOS_CODE(ERRDOS, ERRnotlocked),
+ DOS_CODE(ERRDOS, ERRinvalidpath),
+ DOS_CODE(ERRDOS, ERRcancelviolation),
+ DOS_CODE(ERRDOS, ERRnoatomiclocks),
+ DOS_CODE(ERRDOS, ERRrename),
+ DOS_CODE(ERRDOS, ERRbadpipe),
+ DOS_CODE(ERRDOS, ERRpipebusy),
+ DOS_CODE(ERRDOS, ERRpipeclosing),
+ DOS_CODE(ERRDOS, ERRnotconnected),
+ DOS_CODE(ERRDOS, ERRmoredata),
+ DOS_CODE(ERRDOS, ERRnomoreitems),
+ DOS_CODE(ERRDOS, ERRbaddirectory),
+ DOS_CODE(ERRDOS, ERReasnotsupported),
+ DOS_CODE(ERRDOS, ERRlogonfailure),
+ DOS_CODE(ERRDOS, ERRbuftoosmall),
+ DOS_CODE(ERRDOS, ERRunknownipc),
+ DOS_CODE(ERRDOS, ERRnosuchprintjob),
+ DOS_CODE(ERRDOS, ERRinvgroup),
+ DOS_CODE(ERRDOS, ERRnoipc),
+ DOS_CODE(ERRDOS, ERRdriveralreadyinstalled),
+ DOS_CODE(ERRDOS, ERRunknownprinterport),
+ DOS_CODE(ERRDOS, ERRunknownprinterdriver),
+ DOS_CODE(ERRDOS, ERRunknownprintprocessor),
+ DOS_CODE(ERRDOS, ERRinvalidseparatorfile),
+ DOS_CODE(ERRDOS, ERRinvalidjobpriority),
+ DOS_CODE(ERRDOS, ERRinvalidprintername),
+ DOS_CODE(ERRDOS, ERRprinteralreadyexists),
+ DOS_CODE(ERRDOS, ERRinvalidprintercommand),
+ DOS_CODE(ERRDOS, ERRinvaliddatatype),
+ DOS_CODE(ERRDOS, ERRinvalidenvironment),
+ DOS_CODE(ERRDOS, ERRunknownprintmonitor),
+ DOS_CODE(ERRDOS, ERRprinterdriverinuse),
+ DOS_CODE(ERRDOS, ERRspoolfilenotfound),
+ DOS_CODE(ERRDOS, ERRnostartdoc),
+ DOS_CODE(ERRDOS, ERRnoaddjob),
+ DOS_CODE(ERRDOS, ERRprintprocessoralreadyinstalled),
+ DOS_CODE(ERRDOS, ERRprintmonitoralreadyinstalled),
+ DOS_CODE(ERRDOS, ERRinvalidprintmonitor),
+ DOS_CODE(ERRDOS, ERRprintmonitorinuse),
+ DOS_CODE(ERRDOS, ERRprinterhasjobsqueued),
+ DOS_CODE(ERRDOS, ERReainconsistent),
+
+ DOS_CODE(ERRSRV, ERRerror),
+ DOS_CODE(ERRSRV, ERRbadpw),
+ DOS_CODE(ERRSRV, ERRbadtype),
+ DOS_CODE(ERRSRV, ERRaccess),
+ DOS_CODE(ERRSRV, ERRinvnid),
+ DOS_CODE(ERRSRV, ERRinvnetname),
+ DOS_CODE(ERRSRV, ERRinvdevice),
+ DOS_CODE(ERRSRV, ERRqfull),
+ DOS_CODE(ERRSRV, ERRqtoobig),
+ DOS_CODE(ERRSRV, ERRinvpfid),
+ DOS_CODE(ERRSRV, ERRsmbcmd),
+ DOS_CODE(ERRSRV, ERRsrverror),
+ DOS_CODE(ERRSRV, ERRfilespecs),
+ DOS_CODE(ERRSRV, ERRbadlink),
+ DOS_CODE(ERRSRV, ERRbadpermits),
+ DOS_CODE(ERRSRV, ERRbadpid),
+ DOS_CODE(ERRSRV, ERRsetattrmode),
+ DOS_CODE(ERRSRV, ERRpaused),
+ DOS_CODE(ERRSRV, ERRmsgoff),
+ DOS_CODE(ERRSRV, ERRnoroom),
+ DOS_CODE(ERRSRV, ERRrmuns),
+ DOS_CODE(ERRSRV, ERRtimeout),
+ DOS_CODE(ERRSRV, ERRnoresource),
+ DOS_CODE(ERRSRV, ERRtoomanyuids),
+ DOS_CODE(ERRSRV, ERRbaduid),
+ DOS_CODE(ERRSRV, ERRuseMPX),
+ DOS_CODE(ERRSRV, ERRuseSTD),
+ DOS_CODE(ERRSRV, ERRcontMPX),
+ DOS_CODE(ERRSRV, ERRnosupport),
+ DOS_CODE(ERRSRV, ERRunknownsmb),
+
+ DOS_CODE(ERRHRD, ERRnowrite),
+ DOS_CODE(ERRHRD, ERRbadunit),
+ DOS_CODE(ERRHRD, ERRnotready),
+ DOS_CODE(ERRHRD, ERRbadcmd),
+ DOS_CODE(ERRHRD, ERRdata),
+ DOS_CODE(ERRHRD, ERRbadreq),
+ DOS_CODE(ERRHRD, ERRseek),
+ DOS_CODE(ERRHRD, ERRbadmedia),
+ DOS_CODE(ERRHRD, ERRbadsector),
+ DOS_CODE(ERRHRD, ERRnopaper),
+ DOS_CODE(ERRHRD, ERRwrite),
+ DOS_CODE(ERRHRD, ERRread),
+ DOS_CODE(ERRHRD, ERRgeneral),
+ DOS_CODE(ERRHRD, ERRwrongdisk),
+ DOS_CODE(ERRHRD, ERRFCBunavail),
+ DOS_CODE(ERRHRD, ERRsharebufexc),
+ DOS_CODE(ERRHRD, ERRdiskfull),
+
+ LDAP_CODE(LDAP_SUCCESS),
+ LDAP_CODE(LDAP_OPERATIONS_ERROR),
+ LDAP_CODE(LDAP_PROTOCOL_ERROR),
+ LDAP_CODE(LDAP_TIME_LIMIT_EXCEEDED),
+ LDAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED),
+ LDAP_CODE(LDAP_COMPARE_FALSE),
+ LDAP_CODE(LDAP_COMPARE_TRUE),
+ LDAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED),
+ LDAP_CODE(LDAP_STRONG_AUTH_REQUIRED),
+ LDAP_CODE(LDAP_REFERRAL),
+ LDAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED),
+ LDAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION),
+ LDAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED),
+ LDAP_CODE(LDAP_SASL_BIND_IN_PROGRESS),
+ LDAP_CODE(LDAP_NO_SUCH_ATTRIBUTE),
+ LDAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE),
+ LDAP_CODE(LDAP_INAPPROPRIATE_MATCHING),
+ LDAP_CODE(LDAP_CONSTRAINT_VIOLATION),
+ LDAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS),
+ LDAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX),
+ LDAP_CODE(LDAP_NO_SUCH_OBJECT),
+ LDAP_CODE(LDAP_ALIAS_PROBLEM),
+ LDAP_CODE(LDAP_INVALID_DN_SYNTAX),
+ LDAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM),
+ LDAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION),
+ LDAP_CODE(LDAP_INVALID_CREDENTIALS),
+ LDAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTS),
+ LDAP_CODE(LDAP_BUSY),
+ LDAP_CODE(LDAP_UNAVAILABLE),
+ LDAP_CODE(LDAP_UNWILLING_TO_PERFORM),
+ LDAP_CODE(LDAP_LOOP_DETECT),
+ LDAP_CODE(LDAP_NAMING_VIOLATION),
+ LDAP_CODE(LDAP_OBJECT_CLASS_VIOLATION),
+ LDAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF),
+ LDAP_CODE(LDAP_NOT_ALLOWED_ON_RDN),
+ LDAP_CODE(LDAP_ENTRY_ALREADY_EXISTS),
+ LDAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED),
+ LDAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS),
+ LDAP_CODE(LDAP_OTHER),
+
+ { NULL, NT_STATUS(0) }
+};
+
+static const nt_err_code_struct nt_err_desc[] =
+{
+ { "Success", NT_STATUS_OK },
+ { "Undetermined error", NT_STATUS_UNSUCCESSFUL },
+ { "Access denied", NT_STATUS_ACCESS_DENIED },
+ { "Account locked out", NT_STATUS_ACCOUNT_LOCKED_OUT },
+ { "Must change password", NT_STATUS_PASSWORD_MUST_CHANGE },
+ { "Password is too short", NT_STATUS_PWD_TOO_SHORT },
+ { "Password is too recent", NT_STATUS_PWD_TOO_RECENT },
+ { "Password history conflict", NT_STATUS_PWD_HISTORY_CONFLICT },
+ { "No logon servers", NT_STATUS_NO_LOGON_SERVERS },
+ { "Improperly formed account name", NT_STATUS_INVALID_ACCOUNT_NAME },
+ { "User exists", NT_STATUS_USER_EXISTS },
+ { "No such user", NT_STATUS_NO_SUCH_USER },
+ { "Group exists", NT_STATUS_GROUP_EXISTS },
+ { "No such group", NT_STATUS_NO_SUCH_GROUP },
+ { "Member not in group", NT_STATUS_MEMBER_NOT_IN_GROUP },
+ { "Wrong Password", NT_STATUS_WRONG_PASSWORD },
+ { "Ill formed password", NT_STATUS_ILL_FORMED_PASSWORD },
+ { "Password restriction", NT_STATUS_PASSWORD_RESTRICTION },
+ { "Logon failure", NT_STATUS_LOGON_FAILURE },
+ { "Account restriction", NT_STATUS_ACCOUNT_RESTRICTION },
+ { "Invalid logon hours", NT_STATUS_INVALID_LOGON_HOURS },
+ { "Invalid workstation", NT_STATUS_INVALID_WORKSTATION },
+ { "Password expired", NT_STATUS_PASSWORD_EXPIRED },
+ { "Account disabled", NT_STATUS_ACCOUNT_DISABLED },
+ { "Unexpected information received", NT_STATUS_INVALID_PARAMETER },
+ { "Memory allocation error", NT_STATUS_NO_MEMORY },
+ { "No domain controllers located", NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND },
+ { "Account locked out", NT_STATUS_ACCOUNT_LOCKED_OUT },
+ { "Named pipe not available", NT_STATUS_PIPE_NOT_AVAILABLE },
+ { "Not implemented", NT_STATUS_NOT_IMPLEMENTED },
+ { "Invalid information class", NT_STATUS_INVALID_INFO_CLASS },
+ { "Information length mismatch", NT_STATUS_INFO_LENGTH_MISMATCH },
+ { "Access violation", NT_STATUS_ACCESS_VIOLATION },
+ { "Invalid handle", NT_STATUS_INVALID_HANDLE },
+ { "Invalid parameter", NT_STATUS_INVALID_PARAMETER },
+ { "No memory", NT_STATUS_NO_MEMORY },
+ { "Buffer too small", NT_STATUS_BUFFER_TOO_SMALL },
+ { "Revision mismatch", NT_STATUS_REVISION_MISMATCH },
+ { "No logon servers", NT_STATUS_NO_LOGON_SERVERS },
+ { "No such logon session", NT_STATUS_NO_SUCH_LOGON_SESSION },
+ { "No such privilege", NT_STATUS_NO_SUCH_PRIVILEGE },
+ { "Procedure not found", NT_STATUS_PROCEDURE_NOT_FOUND },
+ { "Server disabled", NT_STATUS_SERVER_DISABLED },
+ { "Invalid pipe state", NT_STATUS_INVALID_PIPE_STATE },
+ { "Named pipe busy", NT_STATUS_PIPE_BUSY },
+ { "Illegal function", NT_STATUS_ILLEGAL_FUNCTION },
+ { "Named pipe dicconnected", NT_STATUS_PIPE_DISCONNECTED },
+ { "Named pipe closing", NT_STATUS_PIPE_CLOSING },
+ { "Remote host not listening", NT_STATUS_REMOTE_NOT_LISTENING },
+ { "Duplicate name on network", NT_STATUS_DUPLICATE_NAME },
+ { "Print queue is full", NT_STATUS_PRINT_QUEUE_FULL },
+ { "No print spool space available", NT_STATUS_NO_SPOOL_SPACE },
+ { "Too many names", NT_STATUS_TOO_MANY_NAMES },
+ { "Too many sessions", NT_STATUS_TOO_MANY_SESSIONS },
+ { "Invalid server state", NT_STATUS_INVALID_SERVER_STATE },
+ { "Invalid domain state", NT_STATUS_INVALID_DOMAIN_STATE },
+ { "Invalid domain role", NT_STATUS_INVALID_DOMAIN_ROLE },
+ { "No such domain", NT_STATUS_NO_SUCH_DOMAIN },
+ { "Domain exists", NT_STATUS_DOMAIN_EXISTS },
+ { "Domain limit exceeded", NT_STATUS_DOMAIN_LIMIT_EXCEEDED },
+ { "Bad logon session state", NT_STATUS_BAD_LOGON_SESSION_STATE },
+ { "Logon session collision", NT_STATUS_LOGON_SESSION_COLLISION },
+ { "Invalid logon type", NT_STATUS_INVALID_LOGON_TYPE },
+ { "Cancelled", NT_STATUS_CANCELLED },
+ { "Invalid computer name", NT_STATUS_INVALID_COMPUTER_NAME },
+ { "Logon server conflict", NT_STATUS_LOGON_SERVER_CONFLICT },
+ { "Time difference at domain controller", NT_STATUS_TIME_DIFFERENCE_AT_DC },
+ { "Pipe broken", NT_STATUS_PIPE_BROKEN },
+ { "Registry corrupt", NT_STATUS_REGISTRY_CORRUPT },
+ { "Too many secrets", NT_STATUS_TOO_MANY_SECRETS },
+ { "Too many SIDs", NT_STATUS_TOO_MANY_SIDS },
+ { "Lanmanager cross encryption required", NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED },
+ { "Log file full", NT_STATUS_LOG_FILE_FULL },
+ { "No trusted LSA secret", NT_STATUS_NO_TRUST_LSA_SECRET },
+ { "No trusted SAM account", NT_STATUS_NO_TRUST_SAM_ACCOUNT },
+ { "Trusted domain failure", NT_STATUS_TRUSTED_DOMAIN_FAILURE },
+ { "Trust relationship failure", NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE },
+ { "Trust failure", NT_STATUS_TRUST_FAILURE },
+ { "Netlogon service not started", NT_STATUS_NETLOGON_NOT_STARTED },
+ { "Account expired", NT_STATUS_ACCOUNT_EXPIRED },
+ { "Network credential conflict", NT_STATUS_NETWORK_CREDENTIAL_CONFLICT },
+ { "Remote session limit", NT_STATUS_REMOTE_SESSION_LIMIT },
+ { "No logon interdomain trust account", NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT },
+ { "No logon workstation trust account", NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT },
+ { "No logon server trust account", NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT },
+ { "Domain trust inconsistent", NT_STATUS_DOMAIN_TRUST_INCONSISTENT },
+ { "No user session key available", NT_STATUS_NO_USER_SESSION_KEY },
+ { "User session deleted", NT_STATUS_USER_SESSION_DELETED },
+ { "Insufficient server resources", NT_STATUS_INSUFF_SERVER_RESOURCES },
+ { "Insufficient logon information", NT_STATUS_INSUFFICIENT_LOGON_INFO },
+
+ { "License quota exceeded", NT_STATUS_LICENSE_QUOTA_EXCEEDED },
+
+ { NULL, NT_STATUS(0) }
+};
+
+/*****************************************************************************
+ returns an NT error message. not amazingly helpful, but better than a number.
+ *****************************************************************************/
+const char *nt_errstr(NTSTATUS nt_code)
+{
+ static char msg[40];
+ int idx = 0;
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (NT_STATUS_V(nt_errs[idx].nt_errcode) ==
+ NT_STATUS_V(nt_code)) {
+ return nt_errs[idx].nt_errstr;
+ }
+ idx++;
+ }
+
+ if (NT_STATUS_IS_LDAP(nt_code)) {
+ slprintf(msg, sizeof(msg), "LDAP code %u", NT_STATUS_LDAP_CODE(nt_code));
+ return msg;
+ }
+
+ slprintf(msg, sizeof(msg), "NT code 0x%08x", NT_STATUS_V(nt_code));
+
+ return msg;
+}
+
+/************************************************************************
+ Print friendler version fo NT error code
+ ***********************************************************************/
+const char *get_friendly_nt_error_msg(NTSTATUS nt_code)
+{
+ int idx = 0;
+
+ while (nt_err_desc[idx].nt_errstr != NULL) {
+ if (NT_STATUS_V(nt_err_desc[idx].nt_errcode) == NT_STATUS_V(nt_code)) {
+ return nt_err_desc[idx].nt_errstr;
+ }
+ idx++;
+ }
+
+ /* fall back to NT_STATUS_XXX string */
+ return nt_errstr(nt_code);
+}
+
+/*****************************************************************************
+ returns an NT_STATUS constant as a string for inclusion in autogen C code
+ *****************************************************************************/
+const char *get_nt_error_c_code(NTSTATUS nt_code)
+{
+ static char out[40];
+ int idx = 0;
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (NT_STATUS_V(nt_errs[idx].nt_errcode) ==
+ NT_STATUS_V(nt_code)) {
+ return nt_errs[idx].nt_errstr;
+ }
+ idx++;
+ }
+
+ slprintf(out, sizeof(out), "NT_STATUS(0x%08x)", NT_STATUS_V(nt_code));
+
+ return out;
+}
+
+/*****************************************************************************
+ returns the NT_STATUS constant matching the string supplied (as an NTSTATUS)
+ *****************************************************************************/
+NTSTATUS nt_status_string_to_code(const char *nt_status_str)
+{
+ int idx = 0;
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (strcasecmp(nt_errs[idx].nt_errstr, nt_status_str) == 0) {
+ return nt_errs[idx].nt_errcode;
+ }
+ idx++;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source4/libcli/util/pyerrors.h b/source4/libcli/util/pyerrors.h
new file mode 100644
index 0000000000..66823f4a41
--- /dev/null
+++ b/source4/libcli/util/pyerrors.h
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 __PYERRORS_H__
+#define __PYERRORS_H__
+
+#define PyErr_FromWERROR(err) Py_BuildValue("(i,s)", W_ERROR_V(err), discard_const_p(char, win_errstr(err)))
+
+#define PyErr_FromNTSTATUS(status) Py_BuildValue("(i,s)", NT_STATUS_V(status), discard_const_p(char, get_friendly_nt_error_msg(status)))
+
+#define PyErr_SetWERROR(err) \
+ PyErr_SetObject(PyExc_RuntimeError, PyErr_FromWERROR(err))
+
+#define PyErr_SetNTSTATUS(status) \
+ PyErr_SetObject(PyExc_RuntimeError, PyErr_FromNTSTATUS(status))
+
+#define PyErr_NTSTATUS_IS_ERR_RAISE(status) \
+ if (NT_STATUS_IS_ERR(status)) { \
+ PyErr_SetNTSTATUS(status); \
+ return NULL; \
+ }
+
+#define PyErr_WERROR_IS_ERR_RAISE(status) \
+ if (!W_ERROR_IS_OK(status)) { \
+ PyErr_SetWERROR(status); \
+ return NULL; \
+ }
+
+#endif /* __PYERRORS_H__ */
diff --git a/source4/libcli/wbclient/config.mk b/source4/libcli/wbclient/config.mk
new file mode 100644
index 0000000000..00df5dbb22
--- /dev/null
+++ b/source4/libcli/wbclient/config.mk
@@ -0,0 +1,5 @@
+[SUBSYSTEM::LIBWBCLIENT]
+PUBLIC_DEPENDENCIES = LIBSAMBA-ERRORS LIBEVENTS
+PRIVATE_DEPENDENCIES = NDR_WINBIND MESSAGING
+
+LIBWBCLIENT_OBJ_FILES = $(libclisrcdir)/wbclient/wbclient.o
diff --git a/source4/libcli/wbclient/wbclient.c b/source4/libcli/wbclient/wbclient.c
new file mode 100644
index 0000000000..da7d678da9
--- /dev/null
+++ b/source4/libcli/wbclient/wbclient.c
@@ -0,0 +1,210 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client library.
+
+ Copyright (C) 2008 Kai Blin <kai@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 "includes.h"
+#include "libcli/wbclient/wbclient.h"
+
+/**
+ * Get the server_id of the winbind task.
+ *
+ * \param[in] msg_ctx message context to use
+ * \param[in] mem_ctx talloc context to use
+ * \param[out] ids array of server_id structs containing the winbind id
+ * \return NT_STATUS_OK on success, NT_STATUS_INTERNAL_ERROR on failure
+ */
+static NTSTATUS get_server_id(struct messaging_context *msg_ctx,
+ TALLOC_CTX *mem_ctx, struct server_id **ids)
+{
+ *ids = irpc_servers_byname(msg_ctx, mem_ctx, "winbind_server");
+ if (*ids == NULL || (*ids)[0].id == 0) {
+ DEBUG(0, ("Geting the winbind server ID failed.\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Initialize the wbclient context, talloc_free() when done.
+ *
+ * \param mem_ctx talloc context to allocate memory from
+ * \param msg_ctx message context to use
+ * \param
+ */
+struct wbc_context *wbc_init(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ struct tevent_context *event_ctx)
+{
+ struct wbc_context *ctx;
+ NTSTATUS status;
+
+ ctx = talloc(mem_ctx, struct wbc_context);
+ if (ctx == NULL) return NULL;
+
+ status = get_server_id(msg_ctx, mem_ctx, &ctx->ids);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ ctx->msg_ctx = msg_ctx;
+ ctx->event_ctx = event_ctx;
+
+ return ctx;
+}
+
+struct wbc_idmap_state {
+ struct composite_context *ctx;
+ struct winbind_get_idmap *req;
+ struct irpc_request *irpc_req;
+ struct id_mapping *ids;
+};
+
+static void sids_to_xids_recv_ids(struct irpc_request *req);
+
+struct composite_context *wbc_sids_to_xids_send(struct wbc_context *wbc_ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t count,
+ struct id_mapping *ids)
+{
+ struct composite_context *ctx;
+ struct wbc_idmap_state *state;
+
+ DEBUG(5, ("wbc_sids_to_xids called\n"));
+
+ ctx = composite_create(mem_ctx, wbc_ctx->event_ctx);
+ if (ctx == NULL) return NULL;
+
+ state = talloc(ctx, struct wbc_idmap_state);
+ if (composite_nomem(state, ctx)) return ctx;
+ ctx->private_data = state;
+
+ state->req = talloc(state, struct winbind_get_idmap);
+ if (composite_nomem(state->req, ctx)) return ctx;
+
+ state->req->in.count = count;
+ state->req->in.level = WINBIND_IDMAP_LEVEL_SIDS_TO_XIDS;
+ state->req->in.ids = ids;
+ state->ctx = ctx;
+
+ state->irpc_req = IRPC_CALL_SEND(wbc_ctx->msg_ctx, wbc_ctx->ids[0],
+ winbind, WINBIND_GET_IDMAP, state->req,
+ state);
+ if (composite_nomem(state->irpc_req, ctx)) return ctx;
+
+ composite_continue_irpc(ctx, state->irpc_req, sids_to_xids_recv_ids,
+ state);
+ return ctx;
+}
+
+static void sids_to_xids_recv_ids(struct irpc_request *req)
+{
+ struct wbc_idmap_state *state = talloc_get_type_abort(
+ req->async.private_data,
+ struct wbc_idmap_state);
+
+ state->ctx->status = irpc_call_recv(state->irpc_req);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->ids = state->req->out.ids;
+ composite_done(state->ctx);
+}
+
+NTSTATUS wbc_sids_to_xids_recv(struct composite_context *ctx,
+ struct id_mapping **ids)
+{
+ NTSTATUS status = composite_wait(ctx);
+ DEBUG(5, ("wbc_sids_to_xids_recv called\n"));
+ if (NT_STATUS_IS_OK(status)) {
+ struct wbc_idmap_state *state = talloc_get_type_abort(
+ ctx->private_data,
+ struct wbc_idmap_state);
+ *ids = state->ids;
+ }
+
+ return status;
+}
+
+static void xids_to_sids_recv_ids(struct irpc_request *req);
+
+struct composite_context *wbc_xids_to_sids_send(struct wbc_context *wbc_ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t count,
+ struct id_mapping *ids)
+{
+ struct composite_context *ctx;
+ struct wbc_idmap_state *state;
+
+ DEBUG(5, ("wbc_xids_to_sids called\n"));
+
+ ctx = composite_create(mem_ctx, wbc_ctx->event_ctx);
+ if (ctx == NULL) return NULL;
+
+ state = talloc(ctx, struct wbc_idmap_state);
+ if (composite_nomem(state, ctx)) return ctx;
+ ctx->private_data = state;
+
+ state->req = talloc(state, struct winbind_get_idmap);
+ if (composite_nomem(state->req, ctx)) return ctx;
+
+ state->req->in.count = count;
+ state->req->in.level = WINBIND_IDMAP_LEVEL_XIDS_TO_SIDS;
+ state->req->in.ids = ids;
+ state->ctx = ctx;
+
+ state->irpc_req = IRPC_CALL_SEND(wbc_ctx->msg_ctx, wbc_ctx->ids[0],
+ winbind, WINBIND_GET_IDMAP, state->req,
+ state);
+ if (composite_nomem(state->irpc_req, ctx)) return ctx;
+
+ composite_continue_irpc(ctx, state->irpc_req, xids_to_sids_recv_ids,
+ state);
+
+ return ctx;
+}
+
+static void xids_to_sids_recv_ids(struct irpc_request *req)
+{
+ struct wbc_idmap_state *state = talloc_get_type_abort(
+ req->async.private_data,
+ struct wbc_idmap_state);
+
+ state->ctx->status = irpc_call_recv(state->irpc_req);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->ids = state->req->out.ids;
+ composite_done(state->ctx);
+}
+
+NTSTATUS wbc_xids_to_sids_recv(struct composite_context *ctx,
+ struct id_mapping **ids)
+{
+ NTSTATUS status = composite_wait(ctx);
+ DEBUG(5, ("wbc_xids_to_sids_recv called\n"));
+ if (NT_STATUS_IS_OK(status)) {
+ struct wbc_idmap_state *state = talloc_get_type_abort(
+ ctx->private_data,
+ struct wbc_idmap_state);
+ *ids = state->ids;
+ }
+
+ return status;
+}
+
diff --git a/source4/libcli/wbclient/wbclient.h b/source4/libcli/wbclient/wbclient.h
new file mode 100644
index 0000000000..416dc78324
--- /dev/null
+++ b/source4/libcli/wbclient/wbclient.h
@@ -0,0 +1,50 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client library.
+
+ Copyright (C) 2008 Kai Blin <kai@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 "lib/messaging/irpc.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
+
+struct wbc_context {
+ struct messaging_context *msg_ctx;
+ struct tevent_context *event_ctx;
+ struct server_id *ids;
+};
+
+struct wbc_context *wbc_init(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ struct tevent_context *event_ctx);
+
+struct composite_context *wbc_sids_to_xids_send(struct wbc_context *wbc_ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t count,
+ struct id_mapping *ids);
+
+NTSTATUS wbc_sids_to_xids_recv(struct composite_context *ctx,
+ struct id_mapping **ids);
+
+struct composite_context *wbc_xids_to_sids_send(struct wbc_context *wbc_ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t count,
+ struct id_mapping *ids);
+
+NTSTATUS wbc_xids_to_sids_recv(struct composite_context *ctx,
+ struct id_mapping **ids);
+
diff --git a/source4/libcli/wrepl/winsrepl.c b/source4/libcli/wrepl/winsrepl.c
new file mode 100644
index 0000000000..48a6abba9d
--- /dev/null
+++ b/source4/libcli/wrepl/winsrepl.c
@@ -0,0 +1,871 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ low level WINS replication client code
+
+ Copyright (C) Andrew Tridgell 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 "includes.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/socket/socket.h"
+#include "libcli/wrepl/winsrepl.h"
+#include "librpc/gen_ndr/ndr_winsrepl.h"
+#include "lib/stream/packet.h"
+#include "libcli/composite/composite.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+
+static struct wrepl_request *wrepl_request_finished(struct wrepl_request *req, NTSTATUS status);
+
+/*
+ mark all pending requests as dead - called when a socket error happens
+*/
+static void wrepl_socket_dead(struct wrepl_socket *wrepl_socket, NTSTATUS status)
+{
+ wrepl_socket->dead = true;
+
+ if (wrepl_socket->packet) {
+ packet_recv_disable(wrepl_socket->packet);
+ packet_set_fde(wrepl_socket->packet, NULL);
+ packet_set_socket(wrepl_socket->packet, NULL);
+ }
+
+ if (wrepl_socket->event.fde) {
+ talloc_free(wrepl_socket->event.fde);
+ wrepl_socket->event.fde = NULL;
+ }
+
+ if (wrepl_socket->sock) {
+ talloc_free(wrepl_socket->sock);
+ wrepl_socket->sock = NULL;
+ }
+
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ while (wrepl_socket->recv_queue) {
+ struct wrepl_request *req = wrepl_socket->recv_queue;
+ DLIST_REMOVE(wrepl_socket->recv_queue, req);
+ wrepl_request_finished(req, status);
+ }
+
+ talloc_set_destructor(wrepl_socket, NULL);
+ if (wrepl_socket->free_skipped) {
+ talloc_free(wrepl_socket);
+ }
+}
+
+static void wrepl_request_timeout_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct wrepl_request *req = talloc_get_type(ptr, struct wrepl_request);
+ wrepl_socket_dead(req->wrepl_socket, NT_STATUS_IO_TIMEOUT);
+}
+
+/*
+ handle recv events
+*/
+static NTSTATUS wrepl_finish_recv(void *private_data, DATA_BLOB packet_blob_in)
+{
+ struct wrepl_socket *wrepl_socket = talloc_get_type(private_data, struct wrepl_socket);
+ struct wrepl_request *req = wrepl_socket->recv_queue;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ if (!req) {
+ DEBUG(1,("Received unexpected WINS packet of length %u!\n",
+ (unsigned)packet_blob_in.length));
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ req->packet = talloc(req, struct wrepl_packet);
+ NT_STATUS_HAVE_NO_MEMORY(req->packet);
+
+ blob.data = packet_blob_in.data + 4;
+ blob.length = packet_blob_in.length - 4;
+
+ /* we have a full request - parse it */
+ ndr_err = ndr_pull_struct_blob(&blob, req->packet, wrepl_socket->iconv_convenience, req->packet,
+ (ndr_pull_flags_fn_t)ndr_pull_wrepl_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ wrepl_request_finished(req, status);
+ return NT_STATUS_OK;
+ }
+
+ if (DEBUGLVL(10)) {
+ DEBUG(10,("Received WINS packet of length %u\n",
+ (unsigned)packet_blob_in.length));
+ NDR_PRINT_DEBUG(wrepl_packet, req->packet);
+ }
+
+ wrepl_request_finished(req, NT_STATUS_OK);
+ return NT_STATUS_OK;
+}
+
+/*
+ handler for winrepl events
+*/
+static void wrepl_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct wrepl_socket *wrepl_socket = talloc_get_type(private_data,
+ struct wrepl_socket);
+ if (flags & EVENT_FD_READ) {
+ packet_recv(wrepl_socket->packet);
+ return;
+ }
+ if (flags & EVENT_FD_WRITE) {
+ packet_queue_run(wrepl_socket->packet);
+ }
+}
+
+static void wrepl_error(void *private_data, NTSTATUS status)
+{
+ struct wrepl_socket *wrepl_socket = talloc_get_type(private_data,
+ struct wrepl_socket);
+ wrepl_socket_dead(wrepl_socket, status);
+}
+
+
+/*
+ destroy a wrepl_socket destructor
+*/
+static int wrepl_socket_destructor(struct wrepl_socket *sock)
+{
+ if (sock->dead) {
+ sock->free_skipped = true;
+ return -1;
+ }
+ wrepl_socket_dead(sock, NT_STATUS_LOCAL_DISCONNECT);
+ return 0;
+}
+
+/*
+ initialise a wrepl_socket. The event_ctx is optional, if provided then
+ operations will use that event context
+*/
+struct wrepl_socket *wrepl_socket_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct smb_iconv_convenience *iconv_convenience)
+{
+ struct wrepl_socket *wrepl_socket;
+ NTSTATUS status;
+
+ wrepl_socket = talloc_zero(mem_ctx, struct wrepl_socket);
+ if (!wrepl_socket) return NULL;
+
+ wrepl_socket->event.ctx = talloc_reference(wrepl_socket, event_ctx);
+ if (!wrepl_socket->event.ctx) goto failed;
+
+ wrepl_socket->iconv_convenience = iconv_convenience;
+
+ status = socket_create("ip", SOCKET_TYPE_STREAM, &wrepl_socket->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ talloc_steal(wrepl_socket, wrepl_socket->sock);
+
+ wrepl_socket->request_timeout = WREPL_SOCKET_REQUEST_TIMEOUT;
+
+ talloc_set_destructor(wrepl_socket, wrepl_socket_destructor);
+
+ return wrepl_socket;
+
+failed:
+ talloc_free(wrepl_socket);
+ return NULL;
+}
+
+/*
+ initialise a wrepl_socket from an already existing connection
+*/
+struct wrepl_socket *wrepl_socket_merge(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_context *sock,
+ struct packet_context *pack)
+{
+ struct wrepl_socket *wrepl_socket;
+
+ wrepl_socket = talloc_zero(mem_ctx, struct wrepl_socket);
+ if (wrepl_socket == NULL) goto failed;
+
+ wrepl_socket->event.ctx = talloc_reference(wrepl_socket, event_ctx);
+ if (wrepl_socket->event.ctx == NULL) goto failed;
+
+ wrepl_socket->sock = sock;
+ talloc_steal(wrepl_socket, wrepl_socket->sock);
+
+
+ wrepl_socket->request_timeout = WREPL_SOCKET_REQUEST_TIMEOUT;
+
+ wrepl_socket->event.fde = event_add_fd(wrepl_socket->event.ctx, wrepl_socket,
+ socket_get_fd(wrepl_socket->sock),
+ EVENT_FD_READ,
+ wrepl_handler, wrepl_socket);
+ if (wrepl_socket->event.fde == NULL) {
+ goto failed;
+ }
+
+ wrepl_socket->packet = pack;
+ talloc_steal(wrepl_socket, wrepl_socket->packet);
+ packet_set_private(wrepl_socket->packet, wrepl_socket);
+ packet_set_socket(wrepl_socket->packet, wrepl_socket->sock);
+ packet_set_callback(wrepl_socket->packet, wrepl_finish_recv);
+ packet_set_full_request(wrepl_socket->packet, packet_full_request_u32);
+ packet_set_error_handler(wrepl_socket->packet, wrepl_error);
+ packet_set_event_context(wrepl_socket->packet, wrepl_socket->event.ctx);
+ packet_set_fde(wrepl_socket->packet, wrepl_socket->event.fde);
+ packet_set_serialise(wrepl_socket->packet);
+
+ talloc_set_destructor(wrepl_socket, wrepl_socket_destructor);
+
+ return wrepl_socket;
+
+failed:
+ talloc_free(wrepl_socket);
+ return NULL;
+}
+
+/*
+ destroy a wrepl_request
+*/
+static int wrepl_request_destructor(struct wrepl_request *req)
+{
+ if (req->state == WREPL_REQUEST_RECV) {
+ DLIST_REMOVE(req->wrepl_socket->recv_queue, req);
+ }
+ req->state = WREPL_REQUEST_ERROR;
+ return 0;
+}
+
+/*
+ wait for a request to complete
+*/
+static NTSTATUS wrepl_request_wait(struct wrepl_request *req)
+{
+ NT_STATUS_HAVE_NO_MEMORY(req);
+ while (req->state < WREPL_REQUEST_DONE) {
+ event_loop_once(req->wrepl_socket->event.ctx);
+ }
+ return req->status;
+}
+
+struct wrepl_connect_state {
+ struct composite_context *result;
+ struct wrepl_socket *wrepl_socket;
+ struct composite_context *creq;
+};
+
+/*
+ handler for winrepl connection completion
+*/
+static void wrepl_connect_handler(struct composite_context *creq)
+{
+ struct wrepl_connect_state *state = talloc_get_type(creq->async.private_data,
+ struct wrepl_connect_state);
+ struct wrepl_socket *wrepl_socket = state->wrepl_socket;
+ struct composite_context *result = state->result;
+
+ result->status = socket_connect_recv(state->creq);
+ if (!composite_is_ok(result)) return;
+
+ wrepl_socket->event.fde = event_add_fd(wrepl_socket->event.ctx, wrepl_socket,
+ socket_get_fd(wrepl_socket->sock),
+ EVENT_FD_READ,
+ wrepl_handler, wrepl_socket);
+ if (composite_nomem(wrepl_socket->event.fde, result)) return;
+
+ /* setup the stream -> packet parser */
+ wrepl_socket->packet = packet_init(wrepl_socket);
+ if (composite_nomem(wrepl_socket->packet, result)) return;
+ packet_set_private(wrepl_socket->packet, wrepl_socket);
+ packet_set_socket(wrepl_socket->packet, wrepl_socket->sock);
+ packet_set_callback(wrepl_socket->packet, wrepl_finish_recv);
+ packet_set_full_request(wrepl_socket->packet, packet_full_request_u32);
+ packet_set_error_handler(wrepl_socket->packet, wrepl_error);
+ packet_set_event_context(wrepl_socket->packet, wrepl_socket->event.ctx);
+ packet_set_fde(wrepl_socket->packet, wrepl_socket->event.fde);
+ packet_set_serialise(wrepl_socket->packet);
+
+ composite_done(result);
+}
+
+const char *wrepl_best_ip(struct loadparm_context *lp_ctx, const char *peer_ip)
+{
+ struct interface *ifaces;
+ load_interfaces(lp_ctx, lp_interfaces(lp_ctx), &ifaces);
+ return iface_best_ip(ifaces, peer_ip);
+}
+
+
+/*
+ connect a wrepl_socket to a WINS server
+*/
+struct composite_context *wrepl_connect_send(struct wrepl_socket *wrepl_socket,
+ const char *our_ip, const char *peer_ip)
+{
+ struct composite_context *result;
+ struct wrepl_connect_state *state;
+ struct socket_address *peer, *us;
+
+ result = talloc_zero(wrepl_socket, struct composite_context);
+ if (!result) return NULL;
+
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+ result->event_ctx = wrepl_socket->event.ctx;
+
+ state = talloc_zero(result, struct wrepl_connect_state);
+ if (composite_nomem(state, result)) return result;
+ result->private_data = state;
+ state->result = result;
+ state->wrepl_socket = wrepl_socket;
+
+ us = socket_address_from_strings(state, wrepl_socket->sock->backend_name,
+ our_ip, 0);
+ if (composite_nomem(us, result)) return result;
+
+ peer = socket_address_from_strings(state, wrepl_socket->sock->backend_name,
+ peer_ip, WINS_REPLICATION_PORT);
+ if (composite_nomem(peer, result)) return result;
+
+ state->creq = socket_connect_send(wrepl_socket->sock, us, peer,
+ 0, wrepl_socket->event.ctx);
+ composite_continue(result, state->creq, wrepl_connect_handler, state);
+ return result;
+}
+
+/*
+ connect a wrepl_socket to a WINS server - recv side
+*/
+NTSTATUS wrepl_connect_recv(struct composite_context *result)
+{
+ struct wrepl_connect_state *state = talloc_get_type(result->private_data,
+ struct wrepl_connect_state);
+ struct wrepl_socket *wrepl_socket = state->wrepl_socket;
+ NTSTATUS status = composite_wait(result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ wrepl_socket_dead(wrepl_socket, status);
+ }
+
+ talloc_free(result);
+ return status;
+}
+
+/*
+ connect a wrepl_socket to a WINS server - sync API
+*/
+NTSTATUS wrepl_connect(struct wrepl_socket *wrepl_socket,
+ const char *our_ip, const char *peer_ip)
+{
+ struct composite_context *c_req = wrepl_connect_send(wrepl_socket, our_ip, peer_ip);
+ return wrepl_connect_recv(c_req);
+}
+
+/*
+ callback from wrepl_request_trigger()
+*/
+static void wrepl_request_trigger_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct wrepl_request *req = talloc_get_type(ptr, struct wrepl_request);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+/*
+ trigger an immediate event on a wrepl_request
+ the return value should only be used in wrepl_request_send()
+ this is the only place where req->trigger is true
+*/
+static struct wrepl_request *wrepl_request_finished(struct wrepl_request *req, NTSTATUS status)
+{
+ struct tevent_timer *te;
+
+ if (req->state == WREPL_REQUEST_RECV) {
+ DLIST_REMOVE(req->wrepl_socket->recv_queue, req);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ req->state = WREPL_REQUEST_ERROR;
+ } else {
+ req->state = WREPL_REQUEST_DONE;
+ }
+
+ req->status = status;
+
+ if (req->trigger) {
+ req->trigger = false;
+ /* a zero timeout means immediate */
+ te = event_add_timed(req->wrepl_socket->event.ctx,
+ req, timeval_zero(),
+ wrepl_request_trigger_handler, req);
+ if (!te) {
+ talloc_free(req);
+ return NULL;
+ }
+ return req;
+ }
+
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return NULL;
+}
+
+struct wrepl_send_ctrl_state {
+ struct wrepl_send_ctrl ctrl;
+ struct wrepl_request *req;
+ struct wrepl_socket *wrepl_sock;
+};
+
+static int wrepl_send_ctrl_destructor(struct wrepl_send_ctrl_state *s)
+{
+ struct wrepl_request *req = s->wrepl_sock->recv_queue;
+
+ /* check if the request is still in WREPL_STATE_RECV,
+ * we need this here because the caller has may called
+ * talloc_free(req) and wrepl_send_ctrl_state isn't
+ * a talloc child of the request, so our s->req pointer
+ * is maybe invalid!
+ */
+ for (; req; req = req->next) {
+ if (req == s->req) break;
+ }
+ if (!req) return 0;
+
+ /* here, we need to make sure the async request handler is called
+ * later in the next event_loop and now now
+ */
+ req->trigger = true;
+ wrepl_request_finished(req, NT_STATUS_OK);
+
+ if (s->ctrl.disconnect_after_send) {
+ wrepl_socket_dead(s->wrepl_sock, NT_STATUS_LOCAL_DISCONNECT);
+ }
+
+ return 0;
+}
+
+/*
+ send a generic wins replication request
+*/
+struct wrepl_request *wrepl_request_send(struct wrepl_socket *wrepl_socket,
+ struct wrepl_packet *packet,
+ struct wrepl_send_ctrl *ctrl)
+{
+ struct wrepl_request *req;
+ struct wrepl_wrap wrap;
+ DATA_BLOB blob;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ req = talloc_zero(wrepl_socket, struct wrepl_request);
+ if (!req) return NULL;
+ req->wrepl_socket = wrepl_socket;
+ req->state = WREPL_REQUEST_RECV;
+ req->trigger = true;
+
+ DLIST_ADD_END(wrepl_socket->recv_queue, req, struct wrepl_request *);
+ talloc_set_destructor(req, wrepl_request_destructor);
+
+ if (wrepl_socket->dead) {
+ return wrepl_request_finished(req, NT_STATUS_INVALID_CONNECTION);
+ }
+
+ wrap.packet = *packet;
+ ndr_err = ndr_push_struct_blob(&blob, req, wrepl_socket->iconv_convenience, &wrap,
+ (ndr_push_flags_fn_t)ndr_push_wrepl_wrap);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ return wrepl_request_finished(req, status);
+ }
+
+ if (DEBUGLVL(10)) {
+ DEBUG(10,("Sending WINS packet of length %u\n",
+ (unsigned)blob.length));
+ NDR_PRINT_DEBUG(wrepl_packet, &wrap.packet);
+ }
+
+ if (wrepl_socket->request_timeout > 0) {
+ req->te = event_add_timed(wrepl_socket->event.ctx, req,
+ timeval_current_ofs(wrepl_socket->request_timeout, 0),
+ wrepl_request_timeout_handler, req);
+ if (!req->te) return wrepl_request_finished(req, NT_STATUS_NO_MEMORY);
+ }
+
+ if (ctrl && (ctrl->send_only || ctrl->disconnect_after_send)) {
+ struct wrepl_send_ctrl_state *s = talloc(blob.data, struct wrepl_send_ctrl_state);
+ if (!s) return wrepl_request_finished(req, NT_STATUS_NO_MEMORY);
+ s->ctrl = *ctrl;
+ s->req = req;
+ s->wrepl_sock = wrepl_socket;
+ talloc_set_destructor(s, wrepl_send_ctrl_destructor);
+ }
+
+ status = packet_send(wrepl_socket->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return wrepl_request_finished(req, status);
+ }
+
+ req->trigger = false;
+ return req;
+}
+
+/*
+ receive a generic WINS replication reply
+*/
+NTSTATUS wrepl_request_recv(struct wrepl_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_packet **packet)
+{
+ NTSTATUS status = wrepl_request_wait(req);
+ if (NT_STATUS_IS_OK(status) && packet) {
+ *packet = talloc_steal(mem_ctx, req->packet);
+ }
+ talloc_free(req);
+ return status;
+}
+
+/*
+ a full WINS replication request/response
+*/
+NTSTATUS wrepl_request(struct wrepl_socket *wrepl_socket,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_packet *req_packet,
+ struct wrepl_packet **reply_packet)
+{
+ struct wrepl_request *req = wrepl_request_send(wrepl_socket, req_packet, NULL);
+ return wrepl_request_recv(req, mem_ctx, reply_packet);
+}
+
+
+/*
+ setup an association - send
+*/
+struct wrepl_request *wrepl_associate_send(struct wrepl_socket *wrepl_socket,
+ struct wrepl_associate *io)
+{
+ struct wrepl_packet *packet;
+ struct wrepl_request *req;
+
+ packet = talloc_zero(wrepl_socket, struct wrepl_packet);
+ if (packet == NULL) return NULL;
+
+ packet->opcode = WREPL_OPCODE_BITS;
+ packet->mess_type = WREPL_START_ASSOCIATION;
+ packet->message.start.minor_version = 2;
+ packet->message.start.major_version = 5;
+
+ /*
+ * nt4 uses 41 bytes for the start_association call
+ * so do it the same and as we don't know th emeanings of this bytes
+ * we just send zeros and nt4, w2k and w2k3 seems to be happy with this
+ *
+ * if we don't do this nt4 uses an old version of the wins replication protocol
+ * and that would break nt4 <-> samba replication
+ */
+ packet->padding = data_blob_talloc(packet, NULL, 21);
+ if (packet->padding.data == NULL) {
+ talloc_free(packet);
+ return NULL;
+ }
+ memset(packet->padding.data, 0, packet->padding.length);
+
+ req = wrepl_request_send(wrepl_socket, packet, NULL);
+
+ talloc_free(packet);
+
+ return req;
+}
+
+/*
+ setup an association - recv
+*/
+NTSTATUS wrepl_associate_recv(struct wrepl_request *req,
+ struct wrepl_associate *io)
+{
+ struct wrepl_packet *packet=NULL;
+ NTSTATUS status;
+ status = wrepl_request_recv(req, req->wrepl_socket, &packet);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (packet->mess_type != WREPL_START_ASSOCIATION_REPLY) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ io->out.assoc_ctx = packet->message.start_reply.assoc_ctx;
+ io->out.major_version = packet->message.start_reply.major_version;
+ }
+ talloc_free(packet);
+ return status;
+}
+
+/*
+ setup an association - sync api
+*/
+NTSTATUS wrepl_associate(struct wrepl_socket *wrepl_socket,
+ struct wrepl_associate *io)
+{
+ struct wrepl_request *req = wrepl_associate_send(wrepl_socket, io);
+ return wrepl_associate_recv(req, io);
+}
+
+
+/*
+ stop an association - send
+*/
+struct wrepl_request *wrepl_associate_stop_send(struct wrepl_socket *wrepl_socket,
+ struct wrepl_associate_stop *io)
+{
+ struct wrepl_packet *packet;
+ struct wrepl_request *req;
+ struct wrepl_send_ctrl ctrl;
+
+ packet = talloc_zero(wrepl_socket, struct wrepl_packet);
+ if (packet == NULL) return NULL;
+
+ packet->opcode = WREPL_OPCODE_BITS;
+ packet->assoc_ctx = io->in.assoc_ctx;
+ packet->mess_type = WREPL_STOP_ASSOCIATION;
+ packet->message.stop.reason = io->in.reason;
+
+ ZERO_STRUCT(ctrl);
+ if (io->in.reason == 0) {
+ ctrl.send_only = true;
+ ctrl.disconnect_after_send = true;
+ }
+
+ req = wrepl_request_send(wrepl_socket, packet, &ctrl);
+
+ talloc_free(packet);
+
+ return req;
+}
+
+/*
+ stop an association - recv
+*/
+NTSTATUS wrepl_associate_stop_recv(struct wrepl_request *req,
+ struct wrepl_associate_stop *io)
+{
+ struct wrepl_packet *packet=NULL;
+ NTSTATUS status;
+ status = wrepl_request_recv(req, req->wrepl_socket, &packet);
+ NT_STATUS_NOT_OK_RETURN(status);
+ talloc_free(packet);
+ return status;
+}
+
+/*
+ setup an association - sync api
+*/
+NTSTATUS wrepl_associate_stop(struct wrepl_socket *wrepl_socket,
+ struct wrepl_associate_stop *io)
+{
+ struct wrepl_request *req = wrepl_associate_stop_send(wrepl_socket, io);
+ return wrepl_associate_stop_recv(req, io);
+}
+
+/*
+ fetch the partner tables - send
+*/
+struct wrepl_request *wrepl_pull_table_send(struct wrepl_socket *wrepl_socket,
+ struct wrepl_pull_table *io)
+{
+ struct wrepl_packet *packet;
+ struct wrepl_request *req;
+
+ packet = talloc_zero(wrepl_socket, struct wrepl_packet);
+ if (packet == NULL) return NULL;
+
+ packet->opcode = WREPL_OPCODE_BITS;
+ packet->assoc_ctx = io->in.assoc_ctx;
+ packet->mess_type = WREPL_REPLICATION;
+ packet->message.replication.command = WREPL_REPL_TABLE_QUERY;
+
+ req = wrepl_request_send(wrepl_socket, packet, NULL);
+
+ talloc_free(packet);
+
+ return req;
+}
+
+
+/*
+ fetch the partner tables - recv
+*/
+NTSTATUS wrepl_pull_table_recv(struct wrepl_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_pull_table *io)
+{
+ struct wrepl_packet *packet=NULL;
+ NTSTATUS status;
+ struct wrepl_table *table;
+ int i;
+
+ status = wrepl_request_recv(req, req->wrepl_socket, &packet);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (packet->mess_type != WREPL_REPLICATION) {
+ status = NT_STATUS_NETWORK_ACCESS_DENIED;
+ } else if (packet->message.replication.command != WREPL_REPL_TABLE_REPLY) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ table = &packet->message.replication.info.table;
+ io->out.num_partners = table->partner_count;
+ io->out.partners = talloc_steal(mem_ctx, table->partners);
+ for (i=0;i<io->out.num_partners;i++) {
+ talloc_steal(io->out.partners, io->out.partners[i].address);
+ }
+
+failed:
+ talloc_free(packet);
+ return status;
+}
+
+
+/*
+ fetch the partner table - sync api
+*/
+NTSTATUS wrepl_pull_table(struct wrepl_socket *wrepl_socket,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_pull_table *io)
+{
+ struct wrepl_request *req = wrepl_pull_table_send(wrepl_socket, io);
+ return wrepl_pull_table_recv(req, mem_ctx, io);
+}
+
+
+/*
+ fetch the names for a WINS partner - send
+*/
+struct wrepl_request *wrepl_pull_names_send(struct wrepl_socket *wrepl_socket,
+ struct wrepl_pull_names *io)
+{
+ struct wrepl_packet *packet;
+ struct wrepl_request *req;
+
+ packet = talloc_zero(wrepl_socket, struct wrepl_packet);
+ if (packet == NULL) return NULL;
+
+ packet->opcode = WREPL_OPCODE_BITS;
+ packet->assoc_ctx = io->in.assoc_ctx;
+ packet->mess_type = WREPL_REPLICATION;
+ packet->message.replication.command = WREPL_REPL_SEND_REQUEST;
+ packet->message.replication.info.owner = io->in.partner;
+
+ req = wrepl_request_send(wrepl_socket, packet, NULL);
+
+ talloc_free(packet);
+
+ return req;
+}
+
+/*
+ fetch the names for a WINS partner - recv
+*/
+NTSTATUS wrepl_pull_names_recv(struct wrepl_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_pull_names *io)
+{
+ struct wrepl_packet *packet=NULL;
+ NTSTATUS status;
+ int i;
+
+ status = wrepl_request_recv(req, req->wrepl_socket, &packet);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (packet->mess_type != WREPL_REPLICATION ||
+ packet->message.replication.command != WREPL_REPL_SEND_REPLY) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ io->out.num_names = packet->message.replication.info.reply.num_names;
+
+ io->out.names = talloc_array(packet, struct wrepl_name, io->out.num_names);
+ if (io->out.names == NULL) goto nomem;
+
+ /* convert the list of names and addresses to a sane format */
+ for (i=0;i<io->out.num_names;i++) {
+ struct wrepl_wins_name *wname = &packet->message.replication.info.reply.names[i];
+ struct wrepl_name *name = &io->out.names[i];
+
+ name->name = *wname->name;
+ talloc_steal(io->out.names, wname->name);
+ name->type = WREPL_NAME_TYPE(wname->flags);
+ name->state = WREPL_NAME_STATE(wname->flags);
+ name->node = WREPL_NAME_NODE(wname->flags);
+ name->is_static = WREPL_NAME_IS_STATIC(wname->flags);
+ name->raw_flags = wname->flags;
+ name->version_id= wname->id;
+ name->owner = talloc_strdup(io->out.names, io->in.partner.address);
+ if (name->owner == NULL) goto nomem;
+
+ /* trying to save 1 or 2 bytes on the wire isn't a good idea */
+ if (wname->flags & 2) {
+ int j;
+
+ name->num_addresses = wname->addresses.addresses.num_ips;
+ name->addresses = talloc_array(io->out.names,
+ struct wrepl_address,
+ name->num_addresses);
+ if (name->addresses == NULL) goto nomem;
+ for (j=0;j<name->num_addresses;j++) {
+ name->addresses[j].owner =
+ talloc_steal(name->addresses,
+ wname->addresses.addresses.ips[j].owner);
+ name->addresses[j].address =
+ talloc_steal(name->addresses,
+ wname->addresses.addresses.ips[j].ip);
+ }
+ } else {
+ name->num_addresses = 1;
+ name->addresses = talloc(io->out.names, struct wrepl_address);
+ if (name->addresses == NULL) goto nomem;
+ name->addresses[0].owner = talloc_strdup(name->addresses,io->in.partner.address);
+ if (name->addresses[0].owner == NULL) goto nomem;
+ name->addresses[0].address = talloc_steal(name->addresses,
+ wname->addresses.ip);
+ }
+ }
+
+ talloc_steal(mem_ctx, io->out.names);
+ talloc_free(packet);
+ return NT_STATUS_OK;
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+failed:
+ talloc_free(packet);
+ return status;
+}
+
+
+
+/*
+ fetch the names for a WINS partner - sync api
+*/
+NTSTATUS wrepl_pull_names(struct wrepl_socket *wrepl_socket,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_pull_names *io)
+{
+ struct wrepl_request *req = wrepl_pull_names_send(wrepl_socket, io);
+ return wrepl_pull_names_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/wrepl/winsrepl.h b/source4/libcli/wrepl/winsrepl.h
new file mode 100644
index 0000000000..ec1fb6bb59
--- /dev/null
+++ b/source4/libcli/wrepl/winsrepl.h
@@ -0,0 +1,162 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ structures for WINS replication client library
+
+ Copyright (C) Andrew Tridgell 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 "librpc/gen_ndr/nbt.h"
+#include "librpc/gen_ndr/winsrepl.h"
+
+/*
+ main context structure for the wins replication client library
+*/
+struct wrepl_socket {
+ struct socket_context *sock;
+ struct packet_context *packet;
+
+ struct {
+ struct tevent_context *ctx;
+ struct tevent_fd *fde;
+ } event;
+
+ /* a queue of replies waiting to be received */
+ struct wrepl_request *recv_queue;
+
+ /* the default timeout for requests, 0 means no timeout */
+#define WREPL_SOCKET_REQUEST_TIMEOUT (60)
+ uint32_t request_timeout;
+
+ /* counter for request timeouts, after 2 timeouts the socket is marked as dead */
+ uint32_t timeout_count;
+
+ /* remember is the socket is dead */
+ bool dead;
+
+ /* remember if we need to free the wrepl_socket at the end of wrepl_socket_dead() */
+ bool free_skipped;
+
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+struct wrepl_send_ctrl {
+ bool send_only;
+ bool disconnect_after_send;
+};
+
+enum wrepl_request_state {
+ WREPL_REQUEST_INIT = 0,
+ WREPL_REQUEST_RECV = 1,
+ WREPL_REQUEST_DONE = 2,
+ WREPL_REQUEST_ERROR = 3
+};
+
+/*
+ a WINS replication request
+*/
+struct wrepl_request {
+ struct wrepl_request *next, *prev;
+ struct wrepl_socket *wrepl_socket;
+
+ enum wrepl_request_state state;
+ bool trigger;
+ NTSTATUS status;
+
+ struct tevent_timer *te;
+
+ struct wrepl_packet *packet;
+
+ struct {
+ void (*fn)(struct wrepl_request *);
+ void *private_data;
+ } async;
+};
+
+
+/*
+ setup an association
+*/
+struct wrepl_associate {
+ struct {
+ uint32_t assoc_ctx;
+ uint16_t major_version;
+ } out;
+};
+
+/*
+ setup an association
+*/
+struct wrepl_associate_stop {
+ struct {
+ uint32_t assoc_ctx;
+ uint32_t reason;
+ } in;
+};
+
+/*
+ pull the partner table
+*/
+struct wrepl_pull_table {
+ struct {
+ uint32_t assoc_ctx;
+ } in;
+ struct {
+ uint32_t num_partners;
+ struct wrepl_wins_owner *partners;
+ } out;
+};
+
+#define WREPL_NAME_TYPE(flags) (flags & WREPL_FLAGS_RECORD_TYPE)
+#define WREPL_NAME_STATE(flags) ((flags & WREPL_FLAGS_RECORD_STATE)>>2)
+#define WREPL_NAME_NODE(flags) ((flags & WREPL_FLAGS_NODE_TYPE)>>5)
+#define WREPL_NAME_IS_STATIC(flags) ((flags & WREPL_FLAGS_IS_STATIC)?true:false)
+
+#define WREPL_NAME_FLAGS(type, state, node, is_static) \
+ (type | (state << 2) | (node << 5) | \
+ (is_static ? WREPL_FLAGS_IS_STATIC : 0))
+
+/*
+ a full pull replication
+*/
+struct wrepl_pull_names {
+ struct {
+ uint32_t assoc_ctx;
+ struct wrepl_wins_owner partner;
+ } in;
+ struct {
+ uint32_t num_names;
+ struct wrepl_name {
+ struct nbt_name name;
+ enum wrepl_name_type type;
+ enum wrepl_name_state state;
+ enum wrepl_name_node node;
+ bool is_static;
+ uint32_t raw_flags;
+ uint64_t version_id;
+ const char *owner;
+ uint32_t num_addresses;
+ struct wrepl_address {
+ const char *owner;
+ const char *address;
+ } *addresses;
+ } *names;
+ } out;
+};
+
+struct resolve_context;
+
+#include "libcli/wrepl/winsrepl_proto.h"