summaryrefslogtreecommitdiffstats
path: root/source4/libcli
diff options
context:
space:
mode:
Diffstat (limited to 'source4/libcli')
-rw-r--r--source4/libcli/cliconnect.c284
-rw-r--r--source4/libcli/clideltree.c146
-rw-r--r--source4/libcli/clifile.c767
-rw-r--r--source4/libcli/clilist.c348
-rw-r--r--source4/libcli/climessage.c104
-rw-r--r--source4/libcli/clireadwrite.c167
-rw-r--r--source4/libcli/clitrans2.c224
-rw-r--r--source4/libcli/composite/composite.c199
-rw-r--r--source4/libcli/composite/composite.h99
-rw-r--r--source4/libcli/dgram/browse.c114
-rw-r--r--source4/libcli/dgram/dgramsocket.c242
-rw-r--r--source4/libcli/dgram/libdgram.h153
-rw-r--r--source4/libcli/dgram/mailslot.c229
-rw-r--r--source4/libcli/dgram/netlogon.c140
-rw-r--r--source4/libcli/finddc.h41
-rw-r--r--source4/libcli/finddcs_cldap.c483
-rw-r--r--source4/libcli/ldap/ldap_bind.c544
-rw-r--r--source4/libcli/ldap/ldap_client.c1069
-rw-r--r--source4/libcli/ldap/ldap_client.h149
-rw-r--r--source4/libcli/ldap/ldap_controls.c1283
-rw-r--r--source4/libcli/ldap/ldap_ildap.c133
-rw-r--r--source4/libcli/ldap/libcli_ldap.h31
-rw-r--r--source4/libcli/ldap/wscript_build11
-rw-r--r--source4/libcli/libcli.h362
-rw-r--r--source4/libcli/rap/rap.c1692
-rw-r--r--source4/libcli/rap/rap.h76
-rw-r--r--source4/libcli/rap/wscript_build7
-rw-r--r--source4/libcli/raw/README5
-rw-r--r--source4/libcli/raw/clierror.c73
-rw-r--r--source4/libcli/raw/clioplock.c66
-rw-r--r--source4/libcli/raw/clisession.c310
-rw-r--r--source4/libcli/raw/clisocket.c459
-rw-r--r--source4/libcli/raw/clitransport.c673
-rw-r--r--source4/libcli/raw/clitree.c228
-rw-r--r--source4/libcli/raw/interfaces.h2867
-rw-r--r--source4/libcli/raw/libcliraw.h343
-rw-r--r--source4/libcli/raw/rawacl.c163
-rw-r--r--source4/libcli/raw/rawdate.c82
-rw-r--r--source4/libcli/raw/raweas.c371
-rw-r--r--source4/libcli/raw/rawfile.c1052
-rw-r--r--source4/libcli/raw/rawfileinfo.c810
-rw-r--r--source4/libcli/raw/rawfsinfo.c431
-rw-r--r--source4/libcli/raw/rawioctl.c173
-rw-r--r--source4/libcli/raw/rawlpq.c48
-rw-r--r--source4/libcli/raw/rawnegotiate.c183
-rw-r--r--source4/libcli/raw/rawnotify.c122
-rw-r--r--source4/libcli/raw/rawreadwrite.c345
-rw-r--r--source4/libcli/raw/rawrequest.c1055
-rw-r--r--source4/libcli/raw/rawsearch.c842
-rw-r--r--source4/libcli/raw/rawsetfileinfo.c492
-rw-r--r--source4/libcli/raw/rawshadow.c82
-rw-r--r--source4/libcli/raw/rawtrans.c446
-rw-r--r--source4/libcli/raw/request.h78
-rw-r--r--source4/libcli/raw/signing.h39
-rw-r--r--source4/libcli/raw/smb.h322
-rw-r--r--source4/libcli/raw/smb_signing.c276
-rw-r--r--source4/libcli/raw/trans2.h309
-rw-r--r--source4/libcli/resolve/bcast.c117
-rw-r--r--source4/libcli/resolve/dns_ex.c671
-rw-r--r--source4/libcli/resolve/host.c60
-rw-r--r--source4/libcli/resolve/lmhosts.c133
-rw-r--r--source4/libcli/resolve/nbtlist.c223
-rw-r--r--source4/libcli/resolve/resolve.c342
-rw-r--r--source4/libcli/resolve/resolve.h53
-rw-r--r--source4/libcli/resolve/resolve_lp.c52
-rw-r--r--source4/libcli/resolve/testsuite.c92
-rw-r--r--source4/libcli/resolve/wins.c83
-rw-r--r--source4/libcli/smb2/break.c74
-rw-r--r--source4/libcli/smb2/cancel.c45
-rw-r--r--source4/libcli/smb2/close.c80
-rw-r--r--source4/libcli/smb2/connect.c483
-rw-r--r--source4/libcli/smb2/create.c446
-rw-r--r--source4/libcli/smb2/find.c181
-rw-r--r--source4/libcli/smb2/flush.c70
-rw-r--r--source4/libcli/smb2/getinfo.c239
-rw-r--r--source4/libcli/smb2/ioctl.c151
-rw-r--r--source4/libcli/smb2/keepalive.c68
-rw-r--r--source4/libcli/smb2/lease_break.c81
-rw-r--r--source4/libcli/smb2/lock.c82
-rw-r--r--source4/libcli/smb2/logoff.c67
-rw-r--r--source4/libcli/smb2/notify.c116
-rw-r--r--source4/libcli/smb2/read.c89
-rw-r--r--source4/libcli/smb2/request.c717
-rw-r--r--source4/libcli/smb2/session.c477
-rw-r--r--source4/libcli/smb2/setinfo.c123
-rw-r--r--source4/libcli/smb2/signing.c139
-rw-r--r--source4/libcli/smb2/smb2.h204
-rw-r--r--source4/libcli/smb2/smb2_calls.h99
-rw-r--r--source4/libcli/smb2/tcon.c52
-rw-r--r--source4/libcli/smb2/tdis.c65
-rw-r--r--source4/libcli/smb2/transport.c557
-rw-r--r--source4/libcli/smb2/util.c363
-rw-r--r--source4/libcli/smb2/write.c81
-rw-r--r--source4/libcli/smb2/wscript_build10
-rw-r--r--source4/libcli/smb_composite/appendacl.c313
-rw-r--r--source4/libcli/smb_composite/connect.c528
-rw-r--r--source4/libcli/smb_composite/connect_nego.c211
-rw-r--r--source4/libcli/smb_composite/fetchfile.c194
-rw-r--r--source4/libcli/smb_composite/fsinfo.c214
-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.c867
-rw-r--r--source4/libcli/smb_composite/smb2.c447
-rw-r--r--source4/libcli/smb_composite/smb_composite.h283
-rw-r--r--source4/libcli/util/clilsa.c548
-rw-r--r--source4/libcli/util/pyerrors.h79
-rw-r--r--source4/libcli/wbclient/wbclient.c193
-rw-r--r--source4/libcli/wbclient/wbclient.h25
-rw-r--r--source4/libcli/wbclient/wscript_build10
-rw-r--r--source4/libcli/wrepl/winsrepl.c1172
-rw-r--r--source4/libcli/wrepl/winsrepl.h110
-rw-r--r--source4/libcli/wscript_build97
112 files changed, 34699 insertions, 0 deletions
diff --git a/source4/libcli/cliconnect.c b/source4/libcli/cliconnect.c
new file mode 100644
index 0000000..58118b0
--- /dev/null
+++ b/source4/libcli/cliconnect.c
@@ -0,0 +1,284 @@
+/*
+ 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"
+#include "libcli/smb/smbXcli_base.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,
+ const char *socket_options,
+ struct nbt_name *calling,
+ struct nbt_name *called)
+{
+ NTSTATUS status;
+
+ cli->options = *options;
+
+ status = smbcli_sock_connect(cli,
+ NULL, /* host_addr */
+ ports,
+ server,
+ resolve_ctx,
+ ev_ctx,
+ socket_options,
+ calling,
+ called,
+ &cli->sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ return true;
+}
+
+/* wrapper around smb_raw_negotiate() */
+NTSTATUS smbcli_negprot(struct smbcli_state *cli, bool unicode, int maxprotocol)
+{
+ if (unicode) {
+ cli->options.unicode = 1;
+ } else {
+ cli->options.unicode = 0;
+ }
+
+ cli->transport = smbcli_transport_init(cli->sock, cli,
+ true, &cli->options);
+ cli->sock = NULL;
+ if (!cli->transport) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return smb_raw_negotiate(cli->transport, unicode, PROTOCOL_CORE, 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 = TCONX_FLAG_EXTENDED_RESPONSE;
+ tcon.tconx.in.flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
+ 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);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ cli->tree->tid = tcon.tconx.out.tid;
+
+ if (tcon.tconx.out.options & SMB_EXTENDED_SIGNATURES) {
+ smb1cli_session_protect_session_key(cli->tree->session->smbXcli);
+ }
+
+out:
+ 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 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,
+ 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;
+
+ if (strncmp(unc_name, "\\\\", 2) &&
+ strncmp(unc_name, "//", 2)) {
+ return false;
+ }
+
+ *hostname = *sharename = NULL;
+
+ *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 0000000..3e4f9fb
--- /dev/null
+++ b/source4/libcli/clideltree.c
@@ -0,0 +1,146 @@
+/*
+ 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;
+ if (asprintf(&s, "%s%s", n, finfo->name) < 0) {
+ free(n);
+ return;
+ }
+
+ 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;
+ if (asprintf(&s2, "%s\\*", s) < 0) {
+ free(s);
+ free(n);
+ return;
+ }
+ 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(status)) {
+ 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 */
+ smbcli_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL, 0);
+ if (NT_STATUS_IS_OK(smbcli_unlink(tree, dname))) {
+ return 1;
+ }
+ }
+
+ if (asprintf(&mask, "%s\\*", dname) < 0) {
+ return -1;
+ }
+ smbcli_unlink_wcard(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 */
+ 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 0000000..c6a5cd5
--- /dev/null
+++ b/source4/libcli/clifile.c
@@ -0,0 +1,767 @@
+/*
+ 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"
+#include "system/dir.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;
+}
+
+/****************************************************************************
+ 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;
+ parms.unlink.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY;
+
+ return smb_raw_unlink(tree, &parms);
+}
+
+struct wcard_delete_state {
+ struct smbcli_tree *tree;
+ NTSTATUS status;
+ char *error_name; /* To help debugging. */
+};
+
+static void del_fn(struct clilist_file_info *finfo,
+ const char *pattern,
+ void *priv)
+{
+ NTSTATUS status;
+ union smb_unlink parms;
+ char *filename = NULL;
+ char *dirname = NULL;
+ char *p = NULL;
+ struct wcard_delete_state *state = (struct wcard_delete_state *)priv;
+
+ if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
+ return;
+ }
+ dirname = talloc_strdup(state, pattern);
+ if (dirname == NULL) {
+ TALLOC_FREE(state->error_name);
+ state->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+ p = strrchr_m(dirname, '\\');
+ if (p != NULL) {
+ /* Remove the terminating '\' */
+ *p = '\0';
+ }
+ if (dirname[0] != '\0') {
+ filename = talloc_asprintf(dirname,
+ "%s\\%s",
+ dirname,
+ finfo->name);
+ } else {
+ filename = talloc_asprintf(dirname,
+ "%s",
+ finfo->name);
+ }
+ if (filename == NULL) {
+ TALLOC_FREE(dirname);
+ TALLOC_FREE(state->error_name);
+ state->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+ parms.unlink.in.pattern = filename;
+ parms.unlink.in.attrib = FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN;
+ status = smb_raw_unlink(state->tree, &parms);
+ if (NT_STATUS_IS_OK(state->status)) {
+ state->status = status;
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * Save off the name we failed to
+ * delete to help debugging.
+ */
+ state->error_name = talloc_move(state, &filename);
+ }
+ }
+ TALLOC_FREE(dirname);
+}
+
+/****************************************************************************
+ Delete a file, possibly with a wildcard pattern.
+****************************************************************************/
+NTSTATUS smbcli_unlink_wcard(struct smbcli_tree *tree, const char *pattern)
+{
+ NTSTATUS status;
+ int ret;
+ struct wcard_delete_state *state = NULL;
+
+ if (strchr(pattern, '*') == NULL) {
+ /* No wildcard, just call smbcli_unlink(). */
+ return smbcli_unlink(tree, pattern);
+ }
+ state = talloc_zero(tree, struct wcard_delete_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->tree = tree;
+ ret = smbcli_list(tree,
+ pattern,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
+ del_fn,
+ state);
+ status = state->status;
+ TALLOC_FREE(state);
+ if (ret < 0) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return status;
+}
+
+/****************************************************************************
+ 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.fnum = 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;
+ unsigned int openfn=0;
+ unsigned int 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;
+ int ret = -1;
+
+ mem_ctx = talloc_init("raw_open");
+ if (!mem_ctx) return ret;
+
+ 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 (NT_STATUS_IS_OK(status)) {
+ if (tmp_path) {
+ *tmp_path = strdup(open_parms.ctemp.out.name);
+ }
+ ret = open_parms.ctemp.out.file.fnum;
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}
diff --git a/source4/libcli/clilist.c b/source4/libcli/clilist.c
new file mode 100644
index 0000000..eb9199d
--- /dev/null
+++ b/source4/libcli/clilist.c
@@ -0,0 +1,348 @@
+/*
+ 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 max_matches = 512;
+ char *mask;
+ int ff_eos = 0, i;
+ 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_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_eos = next_parms.t2fnext.out.end_of_search;
+ received = next_parms.t2fnext.out.count;
+ if (received <= 0) break;
+ if (ff_eos) break;
+ }
+ }
+
+ 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;
+ 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;
+ }
+ }
+
+ 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 0000000..3e9808c
--- /dev/null
+++ b/source4/libcli/climessage.c
@@ -0,0 +1,104 @@
+/*
+ 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);
+ if (req == NULL) {
+ return false;
+ }
+ 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);
+ if (req == NULL) {
+ return false;
+ }
+ 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);
+ if (req == NULL) {
+ return false;
+ }
+ 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 0000000..6ccada9
--- /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 caching
+ 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;
+
+ do {
+ 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;
+ } while (total < size);
+
+ 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 0000000..5c5ba65
--- /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 0000000..3598637
--- /dev/null
+++ b/source4/libcli/composite/composite.c
@@ -0,0 +1,199 @@
+/*
+ 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/smb2/smb2.h"
+#include "libcli/composite/composite.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 = ev;
+
+ 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 (tevent_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) {
+ tevent_add_timer(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) {
+ tevent_add_timer(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) {
+ tevent_add_timer(new_ctx->event_ctx, new_ctx, timeval_zero(), composite_trigger, new_ctx);
+ }
+}
+
+_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;
+ if (new_req->state > SMBCLI_REQUEST_RECV) {
+ composite_error(ctx, new_req->status);
+ 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;
+ if (new_req->state > SMB2_REQUEST_RECV) {
+ composite_error(ctx, new_req->status);
+ 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 0000000..9349cd7
--- /dev/null
+++ b/source4/libcli/composite/composite.h
@@ -0,0 +1,99 @@
+/*
+ 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 smbcli_request;
+struct smb2_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_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/dgram/browse.c b/source4/libcli/dgram/browse.c
new file mode 100644
index 0000000..437b6e1
--- /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, 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, 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, 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)) {
+ (void)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 0000000..154a667
--- /dev/null
+++ b/source4/libcli/dgram/dgramsocket.c
@@ -0,0 +1,242 @@
+/*
+ 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 ((dsize != 0) && (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(5,("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, 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);
+ }
+
+ TEVENT_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 & TEVENT_FD_WRITE) {
+ dgm_socket_send(dgmsock);
+ }
+ if (flags & TEVENT_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 nbt_dgram_socket *dgmsock;
+ NTSTATUS status;
+
+ dgmsock = talloc(mem_ctx, struct nbt_dgram_socket);
+ if (dgmsock == NULL) goto failed;
+
+ dgmsock->event_ctx = event_ctx;
+ if (dgmsock->event_ctx == NULL) goto failed;
+
+ status = socket_create(dgmsock, "ip", SOCKET_TYPE_DGRAM,
+ &dgmsock->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ socket_set_option(dgmsock->sock, "SO_BROADCAST", "1");
+
+ dgmsock->fde = tevent_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;
+
+ 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;
+ TEVENT_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, 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);
+
+ TEVENT_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 0000000..0f313a6
--- /dev/null
+++ b/source4/libcli/dgram/libdgram.h
@@ -0,0 +1,153 @@
+/*
+ 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/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;
+
+ /* 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);
+
+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(TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_netlogon_packet *netlogon);
+
+NTSTATUS dgram_mailslot_netlogon_parse_response(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 0000000..a24800c
--- /dev/null
+++ b/source4/libcli/dgram/mailslot.c
@@ -0,0 +1,229 @@
+/*
+ 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"
+
+#undef strcasecmp
+
+/*
+ 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);
+
+ TEVENT_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);
+ continue;
+ }
+ 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) {
+ talloc_free(tmp_ctx);
+ 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 0000000..1124a33
--- /dev/null
+++ b/source4/libcli/dgram/netlogon.c
@@ -0,0 +1,140 @@
+/*
+ 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, 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, 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 request. The packet must be a valid mailslot packet
+*/
+NTSTATUS dgram_mailslot_netlogon_parse_request(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, 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)) {
+ (void)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(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, netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source4/libcli/finddc.h b/source4/libcli/finddc.h
new file mode 100644
index 0000000..3836d12
--- /dev/null
+++ b/source4/libcli/finddc.h
@@ -0,0 +1,41 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a composite API for finding a DC and its name
+
+ Copyright (C) Andrew Tridgell 2010
+
+ 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/messaging.h"
+#include "libcli/libcli.h"
+#include "libcli/netlogon/netlogon.h"
+
+struct finddcs {
+ struct {
+ const char *domain_name;
+ const char *site_name; /* optional */
+ struct dom_sid *domain_sid; /* optional */
+ uint32_t minimum_dc_flags; /* DS_SERVER_* */
+ const char *server_address; /* optional, bypass name
+ resolution */
+ } in;
+ struct {
+ const char *address; /* IP address of server */
+ struct netlogon_samlogon_response netlogon;
+ } out;
+};
+
+#include "finddcs_proto.h"
diff --git a/source4/libcli/finddcs_cldap.c b/source4/libcli/finddcs_cldap.c
new file mode 100644
index 0000000..b385b1c
--- /dev/null
+++ b/source4/libcli/finddcs_cldap.c
@@ -0,0 +1,483 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a composite API for finding a DC and its name via CLDAP
+
+ Copyright (C) Andrew Tridgell 2010
+ Copyright (C) Andrew Bartlett 2010
+
+ 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 <tevent.h>
+#include "libcli/resolve/resolve.h"
+#include "libcli/cldap/cldap.h"
+#include "libcli/finddc.h"
+#include "libcli/security/security.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/composite/composite.h"
+#include "lib/util/util_net.h"
+
+struct finddcs_cldap_state {
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ const char *domain_name;
+ struct dom_sid *domain_sid;
+ const char *srv_name;
+ const char **srv_addresses;
+ uint32_t minimum_dc_flags;
+ uint32_t srv_address_index;
+ struct cldap_socket *cldap;
+ struct cldap_netlogon *netlogon;
+ NTSTATUS status;
+};
+
+static void finddcs_cldap_srv_resolved(struct composite_context *ctx);
+static void finddcs_cldap_netlogon_replied(struct tevent_req *req);
+static bool finddcs_cldap_srv_lookup(struct finddcs_cldap_state *state,
+ struct finddcs *io,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx);
+static bool finddcs_cldap_nbt_lookup(struct finddcs_cldap_state *state,
+ struct finddcs *io,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx);
+static void finddcs_cldap_nbt_resolved(struct composite_context *ctx);
+static bool finddcs_cldap_name_lookup(struct finddcs_cldap_state *state,
+ struct finddcs *io,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx);
+static void finddcs_cldap_name_resolved(struct composite_context *ctx);
+static void finddcs_cldap_next_server(struct finddcs_cldap_state *state);
+static bool finddcs_cldap_ipaddress(struct finddcs_cldap_state *state, struct finddcs *io);
+
+
+/*
+ * find a list of DCs via DNS/CLDAP
+ */
+struct tevent_req *finddcs_cldap_send(TALLOC_CTX *mem_ctx,
+ struct finddcs *io,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx)
+{
+ struct finddcs_cldap_state *state;
+ struct tevent_req *req;
+
+ req = tevent_req_create(mem_ctx, &state, struct finddcs_cldap_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->req = req;
+ state->ev = event_ctx;
+ state->minimum_dc_flags = io->in.minimum_dc_flags;
+
+ if (io->in.domain_name) {
+ state->domain_name = talloc_strdup(state, io->in.domain_name);
+ if (tevent_req_nomem(state->domain_name, req)) {
+ return tevent_req_post(req, event_ctx);
+ }
+ } else {
+ state->domain_name = NULL;
+ }
+
+ if (io->in.domain_sid) {
+ state->domain_sid = dom_sid_dup(state, io->in.domain_sid);
+ if (tevent_req_nomem(state->domain_sid, req)) {
+ return tevent_req_post(req, event_ctx);
+ }
+ } else {
+ state->domain_sid = NULL;
+ }
+
+ if (io->in.server_address) {
+ if (is_ipaddress(io->in.server_address)) {
+ DEBUG(4,("finddcs: searching for a DC by IP %s\n",
+ io->in.server_address));
+ if (!finddcs_cldap_ipaddress(state, io)) {
+ return tevent_req_post(req, event_ctx);
+ }
+ } else {
+ if (!finddcs_cldap_name_lookup(state, io, resolve_ctx,
+ event_ctx)) {
+ return tevent_req_post(req, event_ctx);
+ }
+ }
+ } else if (io->in.domain_name) {
+ if (strchr(state->domain_name, '.')) {
+ /* looks like a DNS name */
+ DEBUG(4,("finddcs: searching for a DC by DNS domain %s\n", state->domain_name));
+ if (!finddcs_cldap_srv_lookup(state, io, resolve_ctx,
+ event_ctx)) {
+ return tevent_req_post(req, event_ctx);
+ }
+ } else {
+ DEBUG(4,("finddcs: searching for a DC by NBT lookup %s\n", state->domain_name));
+ if (!finddcs_cldap_nbt_lookup(state, io, resolve_ctx,
+ event_ctx)) {
+ return tevent_req_post(req, event_ctx);
+ }
+ }
+ } else {
+ /* either we have the domain name or the IP address */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ DEBUG(2,("finddcs: Please specify at least the domain name or the IP address! \n"));
+ return tevent_req_post(req, event_ctx);
+ }
+
+ return req;
+}
+
+
+/*
+ we've been told the IP of the server, bypass name
+ resolution and go straight to CLDAP
+*/
+static bool finddcs_cldap_ipaddress(struct finddcs_cldap_state *state, struct finddcs *io)
+{
+ NTSTATUS status;
+
+ state->srv_addresses = talloc_array(state, const char *, 2);
+ if (tevent_req_nomem(state->srv_addresses, state->req)) {
+ return false;
+ }
+ state->srv_addresses[0] = talloc_strdup(state->srv_addresses, io->in.server_address);
+ if (tevent_req_nomem(state->srv_addresses[0], state->req)) {
+ return false;
+ }
+ state->srv_addresses[1] = NULL;
+ state->srv_address_index = 0;
+
+ finddcs_cldap_next_server(state);
+ return tevent_req_is_nterror(state->req, &status);
+}
+
+/*
+ start a SRV DNS lookup
+ */
+static bool finddcs_cldap_srv_lookup(struct finddcs_cldap_state *state,
+ struct finddcs *io,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *creq;
+ struct nbt_name name;
+
+ if (io->in.site_name) {
+ state->srv_name = talloc_asprintf(state, "_ldap._tcp.%s._sites.%s",
+ io->in.site_name, io->in.domain_name);
+ } else {
+ state->srv_name = talloc_asprintf(state, "_ldap._tcp.%s", io->in.domain_name);
+ }
+
+ DEBUG(4,("finddcs: looking for SRV records for %s\n", state->srv_name));
+
+ make_nbt_name(&name, state->srv_name, 0);
+
+ creq = resolve_name_ex_send(resolve_ctx, state,
+ RESOLVE_NAME_FLAG_FORCE_DNS | RESOLVE_NAME_FLAG_DNS_SRV,
+ 0, &name, event_ctx);
+ if (tevent_req_nomem(creq, state->req)) {
+ return false;
+ }
+ creq->async.fn = finddcs_cldap_srv_resolved;
+ creq->async.private_data = state;
+
+ return true;
+}
+
+/*
+ start a NBT name lookup for domain<1C>
+ */
+static bool finddcs_cldap_nbt_lookup(struct finddcs_cldap_state *state,
+ struct finddcs *io,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *creq;
+ struct nbt_name name;
+
+ make_nbt_name(&name, state->domain_name, NBT_NAME_LOGON);
+ creq = resolve_name_send(resolve_ctx, state, &name, event_ctx);
+ if (tevent_req_nomem(creq, state->req)) {
+ return false;
+ }
+ creq->async.fn = finddcs_cldap_nbt_resolved;
+ creq->async.private_data = state;
+ return true;
+}
+
+static bool finddcs_cldap_name_lookup(struct finddcs_cldap_state *state,
+ struct finddcs *io,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *creq;
+ struct nbt_name name;
+
+ make_nbt_name(&name, io->in.server_address, NBT_NAME_SERVER);
+ creq = resolve_name_send(resolve_ctx, state, &name, event_ctx);
+ if (tevent_req_nomem(creq, state->req)) {
+ return false;
+ }
+ creq->async.fn = finddcs_cldap_name_resolved;
+ creq->async.private_data = state;
+ return true;
+}
+
+/*
+ fire off a CLDAP query to the next server
+ */
+static void finddcs_cldap_next_server(struct finddcs_cldap_state *state)
+{
+ struct tevent_req *subreq;
+ struct tsocket_address *dest;
+ int ret;
+
+ TALLOC_FREE(state->cldap);
+
+ if (state->srv_addresses[state->srv_address_index] == NULL) {
+ if (NT_STATUS_IS_OK(state->status)) {
+ tevent_req_nterror(state->req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ } else {
+ tevent_req_nterror(state->req, state->status);
+ }
+ DEBUG(2,("finddcs: No matching CLDAP server found\n"));
+ return;
+ }
+
+ /* we should get the port from the SRV response */
+ ret = tsocket_address_inet_from_strings(state, "ip",
+ state->srv_addresses[state->srv_address_index],
+ 389,
+ &dest);
+ if (ret == 0) {
+ state->status = NT_STATUS_OK;
+ } else {
+ state->status = map_nt_error_from_unix_common(errno);
+ }
+ if (!NT_STATUS_IS_OK(state->status)) {
+ state->srv_address_index++;
+ finddcs_cldap_next_server(state);
+ return;
+ }
+
+ state->status = cldap_socket_init(state, NULL, dest, &state->cldap);
+ if (!NT_STATUS_IS_OK(state->status)) {
+ state->srv_address_index++;
+ finddcs_cldap_next_server(state);
+ return;
+ }
+
+ TALLOC_FREE(state->netlogon);
+ state->netlogon = talloc_zero(state, struct cldap_netlogon);
+ if (state->netlogon == NULL) {
+ state->status = NT_STATUS_NO_MEMORY;
+ state->srv_address_index++;
+ finddcs_cldap_next_server(state);
+ return;
+ }
+
+ if ((state->domain_name != NULL) && (strchr(state->domain_name, '.'))) {
+ state->netlogon->in.realm = state->domain_name;
+ }
+ if (state->domain_sid) {
+ state->netlogon->in.domain_sid = dom_sid_string(state, state->domain_sid);
+ if (state->netlogon->in.domain_sid == NULL) {
+ state->status = NT_STATUS_NO_MEMORY;
+ state->srv_address_index++;
+ finddcs_cldap_next_server(state);
+ return;
+ }
+ }
+ state->netlogon->in.acct_control = -1;
+ state->netlogon->in.version =
+ NETLOGON_NT_VERSION_5 |
+ NETLOGON_NT_VERSION_5EX |
+ NETLOGON_NT_VERSION_IP;
+ state->netlogon->in.map_response = true;
+
+ DEBUG(4,("finddcs: performing CLDAP query on %s\n",
+ state->srv_addresses[state->srv_address_index]));
+
+ subreq = cldap_netlogon_send(state, state->ev,
+ state->cldap, state->netlogon);
+ if (subreq == NULL) {
+ state->status = NT_STATUS_NO_MEMORY;
+ state->srv_address_index++;
+ finddcs_cldap_next_server(state);
+ return;
+ }
+
+ tevent_req_set_callback(subreq, finddcs_cldap_netlogon_replied, state);
+}
+
+
+/*
+ we have a response from a CLDAP server for a netlogon request
+ */
+static void finddcs_cldap_netlogon_replied(struct tevent_req *subreq)
+{
+ struct finddcs_cldap_state *state;
+ NTSTATUS status;
+
+ state = tevent_req_callback_data(subreq, struct finddcs_cldap_state);
+
+ status = cldap_netlogon_recv(subreq, state->netlogon, state->netlogon);
+ TALLOC_FREE(subreq);
+ TALLOC_FREE(state->cldap);
+ if (!NT_STATUS_IS_OK(status)) {
+ state->status = status;
+ state->srv_address_index++;
+ finddcs_cldap_next_server(state);
+ return;
+ }
+ if (state->minimum_dc_flags !=
+ (state->minimum_dc_flags & state->netlogon->out.netlogon.data.nt5_ex.server_type)) {
+ /* the server didn't match the minimum requirements */
+ DEBUG(4,("finddcs: Skipping DC %s with server_type=0x%08x - required 0x%08x\n",
+ state->srv_addresses[state->srv_address_index],
+ state->netlogon->out.netlogon.data.nt5_ex.server_type,
+ state->minimum_dc_flags));
+ state->srv_address_index++;
+ finddcs_cldap_next_server(state);
+ return;
+ }
+
+ DEBUG(4,("finddcs: Found matching DC %s with server_type=0x%08x\n",
+ state->srv_addresses[state->srv_address_index],
+ state->netlogon->out.netlogon.data.nt5_ex.server_type));
+
+ tevent_req_done(state->req);
+}
+
+static void finddcs_cldap_name_resolved(struct composite_context *ctx)
+{
+ struct finddcs_cldap_state *state =
+ talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
+ NTSTATUS status;
+ unsigned i;
+
+ status = resolve_name_multiple_recv(ctx, state, &state->srv_addresses);
+ if (tevent_req_nterror(state->req, status)) {
+ DEBUG(2,("finddcs: No matching server found\n"));
+ return;
+ }
+
+ for (i=0; state->srv_addresses[i]; i++) {
+ DEBUG(4,("finddcs: response %u at '%s'\n",
+ i, state->srv_addresses[i]));
+ }
+
+ state->srv_address_index = 0;
+
+ state->status = NT_STATUS_OK;
+ finddcs_cldap_next_server(state);
+}
+
+/*
+ handle NBT name lookup reply
+ */
+static void finddcs_cldap_nbt_resolved(struct composite_context *ctx)
+{
+ struct finddcs_cldap_state *state =
+ talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
+ NTSTATUS status;
+ unsigned i;
+
+ status = resolve_name_multiple_recv(ctx, state, &state->srv_addresses);
+ if (tevent_req_nterror(state->req, status)) {
+ DEBUG(2,("finddcs: No matching NBT <1c> server found\n"));
+ return;
+ }
+
+ for (i=0; state->srv_addresses[i]; i++) {
+ DEBUG(4,("finddcs: NBT <1c> response %u at '%s'\n",
+ i, state->srv_addresses[i]));
+ }
+
+ state->srv_address_index = 0;
+
+ finddcs_cldap_next_server(state);
+}
+
+
+/*
+ * Having got a DNS SRV answer, fire off the first CLDAP request
+ */
+static void finddcs_cldap_srv_resolved(struct composite_context *ctx)
+{
+ struct finddcs_cldap_state *state =
+ talloc_get_type(ctx->async.private_data, struct finddcs_cldap_state);
+ NTSTATUS status;
+ unsigned i;
+
+ status = resolve_name_multiple_recv(ctx, state, &state->srv_addresses);
+ if (tevent_req_nterror(state->req, status)) {
+ DEBUG(2,("finddcs: Failed to find SRV record for %s\n", state->srv_name));
+ return;
+ }
+
+ for (i=0; state->srv_addresses[i]; i++) {
+ DEBUG(4,("finddcs: DNS SRV response %u at '%s'\n", i, state->srv_addresses[i]));
+ }
+
+ state->srv_address_index = 0;
+
+ state->status = NT_STATUS_OK;
+ finddcs_cldap_next_server(state);
+}
+
+
+NTSTATUS finddcs_cldap_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct finddcs *io)
+{
+ struct finddcs_cldap_state *state = tevent_req_data(req, struct finddcs_cldap_state);
+ bool ok;
+ NTSTATUS status;
+
+ ok = tevent_req_poll(req, state->ev);
+ if (!ok) {
+ talloc_free(req);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ talloc_steal(mem_ctx, state->netlogon);
+ io->out.netlogon = state->netlogon->out.netlogon;
+ io->out.address = talloc_steal(
+ mem_ctx, state->srv_addresses[state->srv_address_index]);
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS finddcs_cldap(TALLOC_CTX *mem_ctx,
+ struct finddcs *io,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx)
+{
+ NTSTATUS status;
+ struct tevent_req *req = finddcs_cldap_send(mem_ctx,
+ io,
+ resolve_ctx,
+ event_ctx);
+ status = finddcs_cldap_recv(req, mem_ctx, io);
+ talloc_free(req);
+ return status;
+}
diff --git a/source4/libcli/ldap/ldap_bind.c b/source4/libcli/ldap/ldap_bind.c
new file mode 100644
index 0000000..1008ff2
--- /dev/null
+++ b/source4/libcli/ldap/ldap_bind.c
@@ -0,0 +1,544 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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/libcli_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/gensec/gensec_internal.h" /* TODO: remove this */
+#include "source4/auth/gensec/gensec_tstream.h"
+#include "auth/credentials/credentials.h"
+#include "lib/stream/packet.h"
+#include "param/param.h"
+#include "param/loadparm.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;
+ bool first = true;
+ int wrap_flags = 0;
+ const char **sasl_names;
+ uint32_t old_gensec_features;
+ static const char *supported_sasl_mech_attrs[] = {
+ "supportedSASLMechanisms",
+ NULL
+ };
+ unsigned int logon_retries = 0;
+ size_t queue_length;
+
+ if (conn->sockets.active == NULL) {
+ status = NT_STATUS_CONNECTION_DISCONNECTED;
+ goto failed;
+ }
+
+ queue_length = tevent_queue_length(conn->sockets.send_queue);
+ if (queue_length != 0) {
+ status = NT_STATUS_INVALID_PARAMETER_MIX;
+ DEBUG(1, ("SASL bind triggered with non empty send_queue[%zu]: %s\n",
+ queue_length, nt_errstr(status)));
+ goto failed;
+ }
+
+ if (conn->pending != NULL) {
+ status = NT_STATUS_INVALID_PARAMETER_MIX;
+ DEBUG(1, ("SASL bind triggered with pending requests: %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;
+
+ gensec_init();
+
+ if (conn->sockets.active == conn->sockets.tls) {
+ /*
+ * require Kerberos SIGN/SEAL only if we don't use SSL
+ * Windows seem not to like double encryption
+ */
+ wrap_flags = 0;
+ } else if (cli_credentials_is_anonymous(creds)) {
+ /*
+ * anonymous isn't protected
+ */
+ wrap_flags = 0;
+ } else {
+ wrap_flags = lpcfg_client_ldap_sasl_wrapping(lp_ctx);
+ }
+
+try_logon_again:
+ /*
+ we loop back here on a logon failure, and re-create the
+ gensec session. The logon_retries counter ensures we don't
+ loop forever.
+ */
+ data_blob_free(&input);
+ TALLOC_FREE(conn->gensec);
+
+ status = gensec_client_start(conn, &conn->gensec,
+ lpcfg_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;
+ }
+
+ old_gensec_features = cli_credentials_get_gensec_features(creds);
+ if (wrap_flags == 0) {
+ cli_credentials_set_gensec_features(creds,
+ old_gensec_features & ~(GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL),
+ CRED_SPECIFIED);
+ }
+
+ /* 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,
+ CRED_SPECIFIED);
+
+ if (wrap_flags & ADS_AUTH_SASL_SEAL) {
+ gensec_want_feature(conn->gensec, GENSEC_FEATURE_SIGN);
+ gensec_want_feature(conn->gensec, GENSEC_FEATURE_SEAL);
+ }
+ if (wrap_flags & ADS_AUTH_SASL_SIGN) {
+ gensec_want_feature(conn->gensec, GENSEC_FEATURE_SIGN);
+ }
+
+ /*
+ * This is an indication for the NTLMSSP backend to
+ * also encrypt when only GENSEC_FEATURE_SIGN is requested
+ * in gensec_[un]wrap().
+ */
+ gensec_want_feature(conn->gensec, GENSEC_FEATURE_LDAP_STYLE);
+
+ 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 = 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.
+ *
+ * For SASL/EXTERNAL, there is no data to send, but we still
+ * must send the actual Bind request the first time around.
+ * Otherwise, a result of NT_STATUS_OK with 0 output means the
+ * end of a multi-step authentication, and no message must be
+ * sent.
+ */
+
+ 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) {
+ if (!first)
+ break;
+ }
+ first = false;
+
+ /* 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_reparent(conn, 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_STRONG_AUTH_REQUIRED) {
+ if (wrap_flags == 0) {
+ wrap_flags = ADS_AUTH_SASL_SIGN;
+ goto try_logon_again;
+ }
+ }
+
+ if (result == LDAP_INVALID_CREDENTIALS) {
+ /*
+ try a second time on invalid credentials, to
+ give the user a chance to re-enter the
+ password and to handle the case where our
+ kerberos ticket is invalid as the server
+ password has changed
+ */
+ const char *principal;
+
+ principal = gensec_get_target_principal(conn->gensec);
+ if (principal == NULL) {
+ const char *hostname = gensec_get_target_hostname(conn->gensec);
+ const char *service = gensec_get_target_service(conn->gensec);
+ if (hostname != NULL && service != NULL) {
+ principal = talloc_asprintf(tmp_ctx, "%s/%s", service, hostname);
+ }
+ }
+
+ if (cli_credentials_failed_kerberos_login(creds, principal, &logon_retries) ||
+ cli_credentials_wrong_password(creds)) {
+ /*
+ destroy our gensec session and loop
+ back up to the top to retry,
+ offering the user a chance to enter
+ new credentials, or get a new ticket
+ if using kerberos
+ */
+ goto try_logon_again;
+ }
+ }
+
+ 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)) {
+ goto failed;
+ }
+
+ conn->bind.type = LDAP_BIND_SASL;
+ conn->bind.creds = creds;
+
+ if (wrap_flags & ADS_AUTH_SASL_SEAL) {
+ if (!gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (!gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ } else if (wrap_flags & ADS_AUTH_SASL_SIGN) {
+ if (!gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+
+ if (!gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN) &&
+ !gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL)) {
+ return NT_STATUS_OK;
+ }
+
+ status = gensec_create_tstream(conn->sockets.raw,
+ conn->gensec,
+ conn->sockets.raw,
+ &conn->sockets.sasl);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ conn->sockets.active = conn->sockets.sasl;
+
+ return NT_STATUS_OK;
+
+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 0000000..8614ccd
--- /dev/null
+++ b/source4/libcli/ldap/ldap_client.c
@@ -0,0 +1,1069 @@
+/*
+ Unix SMB/CIFS implementation.
+ 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/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+#include "../lib/util/asn1.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/ldap/libcli_ldap.h"
+#include "libcli/ldap/ldap_proto.h"
+#include "libcli/ldap/ldap_client.h"
+#include "libcli/composite/composite.h"
+#include "lib/tls/tls.h"
+#include "auth/gensec/gensec.h"
+#include "system/time.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+static void ldap_connection_dead(struct ldap_connection *conn, NTSTATUS status);
+
+static int ldap_connection_destructor(struct ldap_connection *conn)
+{
+ /*
+ * NT_STATUS_OK means that callbacks of pending requests are not
+ * triggered
+ */
+ ldap_connection_dead(conn, NT_STATUS_OK);
+ return 0;
+}
+
+/**
+ 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->sockets.send_queue = tevent_queue_create(conn,
+ "ldap_connection send_queue");
+ if (conn->sockets.send_queue == NULL) {
+ TALLOC_FREE(conn);
+ return NULL;
+ }
+
+ conn->lp_ctx = lp_ctx;
+
+ /* set a reasonable request timeout */
+ conn->timeout = 60;
+
+ /* explicitly avoid reconnections by default */
+ conn->reconnect.max_retries = 0;
+
+ talloc_set_destructor(conn, ldap_connection_destructor);
+ return conn;
+}
+
+/*
+ the connection is dead
+*/
+static void ldap_connection_dead(struct ldap_connection *conn, NTSTATUS status)
+{
+ struct ldap_request *req;
+
+ tevent_queue_stop(conn->sockets.send_queue);
+ TALLOC_FREE(conn->sockets.recv_subreq);
+ conn->sockets.active = NULL;
+ TALLOC_FREE(conn->sockets.sasl);
+ TALLOC_FREE(conn->sockets.tls);
+ TALLOC_FREE(conn->sockets.raw);
+
+ /* return an error for any pending request ... */
+ while (conn->pending) {
+ req = conn->pending;
+ DLIST_REMOVE(req->conn->pending, req);
+ req->conn = NULL;
+ req->state = LDAP_REQUEST_DONE;
+ if (NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ req->status = status;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ }
+}
+
+static void ldap_reconnect(struct ldap_connection *conn);
+
+/*
+ handle packet errors
+*/
+static void ldap_error_handler(struct ldap_connection *conn, NTSTATUS status)
+{
+ ldap_connection_dead(conn, status);
+
+ /* 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) {
+ TALLOC_FREE(msg);
+ 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 */
+ req->replies = talloc_realloc(req, req->replies,
+ struct ldap_message *, req->num_replies+1);
+ if (req->replies == NULL) {
+ TALLOC_FREE(msg);
+ 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);
+ }
+}
+
+static void ldap_connection_recv_done(struct tevent_req *subreq);
+
+static void ldap_connection_recv_next(struct ldap_connection *conn)
+{
+ struct tevent_req *subreq = NULL;
+
+ if (conn->sockets.recv_subreq != NULL) {
+ return;
+ }
+
+ if (conn->sockets.active == NULL) {
+ return;
+ }
+
+ if (conn->pending == NULL) {
+ return;
+ }
+
+ /*
+ * The minimum size of a LDAP pdu is 7 bytes
+ *
+ * dumpasn1 -hh ldap-unbind-min.dat
+ *
+ * <30 05 02 01 09 42 00>
+ * 0 5: SEQUENCE {
+ * <02 01 09>
+ * 2 1: INTEGER 9
+ * <42 00>
+ * 5 0: [APPLICATION 2]
+ * : Error: Object has zero length.
+ * : }
+ *
+ * dumpasn1 -hh ldap-unbind-windows.dat
+ *
+ * <30 84 00 00 00 05 02 01 09 42 00>
+ * 0 5: SEQUENCE {
+ * <02 01 09>
+ * 6 1: INTEGER 9
+ * <42 00>
+ * 9 0: [APPLICATION 2]
+ * : Error: Object has zero length.
+ * : }
+ *
+ * This means using an initial read size
+ * of 7 is ok.
+ */
+ subreq = tstream_read_pdu_blob_send(conn,
+ conn->event.event_ctx,
+ conn->sockets.active,
+ 7, /* initial_read_size */
+ ldap_full_packet,
+ conn);
+ if (subreq == NULL) {
+ ldap_error_handler(conn, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ tevent_req_set_callback(subreq, ldap_connection_recv_done, conn);
+ conn->sockets.recv_subreq = subreq;
+ return;
+}
+
+/*
+ decode/process LDAP data
+*/
+static void ldap_connection_recv_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct ldap_connection *conn =
+ tevent_req_callback_data(subreq,
+ struct ldap_connection);
+ struct ldap_message *msg;
+ struct asn1_data *asn1;
+ DATA_BLOB blob;
+ struct ldap_request_limits limits = {0};
+
+ msg = talloc_zero(conn, struct ldap_message);
+ if (msg == NULL) {
+ ldap_error_handler(conn, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ asn1 = asn1_init(conn, ASN1_MAX_TREE_DEPTH);
+ if (asn1 == NULL) {
+ TALLOC_FREE(msg);
+ ldap_error_handler(conn, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ conn->sockets.recv_subreq = NULL;
+
+ status = tstream_read_pdu_blob_recv(subreq,
+ asn1,
+ &blob);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(msg);
+ asn1_free(asn1);
+ ldap_error_handler(conn, status);
+ return;
+ }
+
+ asn1_load_nocopy(asn1, blob.data, blob.length);
+
+ status = ldap_decode(asn1, &limits, samba_ldap_control_handlers(), msg);
+ asn1_free(asn1);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(msg);
+ ldap_error_handler(conn, status);
+ return;
+ }
+
+ ldap_match_message(conn, msg);
+ ldap_connection_recv_next(conn);
+
+ return;
+}
+
+enum ldap_proto {
+ LDAP_PROTO_NONE,
+ LDAP_PROTO_LDAP,
+ LDAP_PROTO_LDAPS,
+ LDAP_PROTO_LDAPI
+};
+
+static int ldap_parse_basic_url(
+ const char *url,
+ enum ldap_proto *pproto,
+ TALLOC_CTX *mem_ctx,
+ char **pdest, /* path for ldapi, host for ldap[s] */
+ uint16_t *pport) /* Not set for ldapi */
+{
+ enum ldap_proto proto = LDAP_PROTO_NONE;
+ char *host = NULL;
+ int ret, port;
+
+ if (url == NULL) {
+ return EINVAL;
+ }
+
+ if (strncasecmp_m(url, "ldapi://", strlen("ldapi://")) == 0) {
+ char *path = NULL, *end = NULL;
+
+ path = talloc_strdup(mem_ctx, url+8);
+ if (path == NULL) {
+ return ENOMEM;
+ }
+ end = rfc1738_unescape(path);
+ if (end == NULL) {
+ TALLOC_FREE(path);
+ return EINVAL;
+ }
+
+ *pproto = LDAP_PROTO_LDAPI;
+ *pdest = path;
+ return 0;
+ }
+
+ if (strncasecmp_m(url, "ldap://", strlen("ldap://")) == 0) {
+ url += 7;
+ proto = LDAP_PROTO_LDAP;
+ port = 389;
+ }
+ if (strncasecmp_m(url, "ldaps://", strlen("ldaps://")) == 0) {
+ url += 8;
+ port = 636;
+ proto = LDAP_PROTO_LDAPS;
+ }
+
+ if (proto == LDAP_PROTO_NONE) {
+ return EPROTONOSUPPORT;
+ }
+
+ if (url[0] == '[') {
+ /*
+ * IPv6 with [aa:bb:cc..]:port
+ */
+ const char *end = NULL;
+
+ url +=1;
+
+ end = strchr(url, ']');
+ if (end == NULL) {
+ return EINVAL;
+ }
+
+ ret = sscanf(end+1, ":%d", &port);
+ if (ret < 0) {
+ return EINVAL;
+ }
+
+ *pdest = talloc_strndup(mem_ctx, url, end-url);
+ if (*pdest == NULL) {
+ return ENOMEM;
+ }
+ *pproto = proto;
+ *pport = port;
+ return 0;
+ }
+
+ ret = sscanf(url, "%m[^:/]:%d", &host, &port);
+ if (ret < 1) {
+ return EINVAL;
+ }
+
+ *pdest = talloc_strdup(mem_ctx, host);
+ SAFE_FREE(host);
+ if (*pdest == NULL) {
+ return ENOMEM;
+ }
+ *pproto = proto;
+ *pport = port;
+
+ return 0;
+}
+
+/*
+ connect to a ldap server
+*/
+
+struct ldap_connect_state {
+ struct composite_context *ctx;
+ struct ldap_connection *conn;
+ struct socket_context *sock;
+ struct tstream_context *raw;
+ struct tstream_tls_params *tls_params;
+ struct tstream_context *tls;
+};
+
+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;
+ enum ldap_proto proto;
+ char *dest = NULL;
+ uint16_t port;
+ 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;
+ }
+
+ ret = ldap_parse_basic_url(url, &proto, conn, &dest, &port);
+ if (ret != 0) {
+ composite_error(result, map_nt_error_from_unix_common(ret));
+ return result;
+ }
+
+ if (proto == LDAP_PROTO_LDAPI) {
+ struct socket_address *unix_addr;
+ NTSTATUS status = socket_create(state, "unix",
+ SOCKET_TYPE_STREAM,
+ &state->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+
+ conn->host = talloc_asprintf(conn, "%s.%s",
+ lpcfg_netbios_name(conn->lp_ctx),
+ lpcfg_dnsdomain(conn->lp_ctx));
+ if (composite_nomem(conn->host, state->ctx)) {
+ return result;
+ }
+
+ unix_addr = socket_address_from_strings(state, state->sock->backend_name,
+ dest, 0);
+ if (composite_nomem(unix_addr, result)) {
+ return result;
+ }
+
+ ctx = socket_connect_send(state->sock, NULL, unix_addr,
+ 0, result->event_ctx);
+ ctx->async.fn = ldap_connect_recv_unix_conn;
+ ctx->async.private_data = state;
+ return result;
+ }
+
+ if ((proto == LDAP_PROTO_LDAP) || (proto == LDAP_PROTO_LDAPS)) {
+
+ conn->ldaps = (proto == LDAP_PROTO_LDAPS);
+
+ conn->host = talloc_move(conn, &dest);
+ conn->port = port;
+
+ if (conn->ldaps) {
+ char *ca_file = lpcfg_tls_cafile(state, conn->lp_ctx);
+ char *crl_file = lpcfg_tls_crlfile(state, conn->lp_ctx);
+ const char *tls_priority = lpcfg_tls_priority(conn->lp_ctx);
+ enum tls_verify_peer_state verify_peer =
+ lpcfg_tls_verify_peer(conn->lp_ctx);
+ NTSTATUS status;
+
+ status = tstream_tls_params_client(state,
+ ca_file,
+ crl_file,
+ tls_priority,
+ verify_peer,
+ conn->host,
+ &state->tls_params);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(result, status);
+ return result;
+ }
+ }
+
+ ctx = socket_connect_multi_send(state, conn->host, 1, &conn->port,
+ lpcfg_resolve_context(conn->lp_ctx),
+ result->event_ctx);
+ if (composite_nomem(ctx, result)) {
+ return result;
+ }
+
+ 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_tls(struct tevent_req *subreq);
+
+static void ldap_connect_got_sock(struct composite_context *ctx,
+ struct ldap_connection *conn)
+{
+ struct ldap_connect_state *state =
+ talloc_get_type_abort(ctx->private_data,
+ struct ldap_connect_state);
+ struct tevent_req *subreq = NULL;
+ int fd;
+ int ret;
+
+ socket_set_flags(state->sock, SOCKET_FLAG_NOCLOSE);
+ fd = socket_get_fd(state->sock);
+ TALLOC_FREE(state->sock);
+
+ smb_set_close_on_exec(fd);
+
+ ret = set_blocking(fd, false);
+ if (ret == -1) {
+ NTSTATUS status = map_nt_error_from_unix_common(errno);
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ ret = tstream_bsd_existing_socket(state, fd, &state->raw);
+ if (ret == -1) {
+ NTSTATUS status = map_nt_error_from_unix_common(errno);
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ if (!conn->ldaps) {
+ conn->sockets.raw = talloc_move(conn, &state->raw);
+ conn->sockets.active = conn->sockets.raw;
+ composite_done(state->ctx);
+ return;
+ }
+
+ subreq = tstream_tls_connect_send(state, state->ctx->event_ctx,
+ state->raw, state->tls_params);
+ if (composite_nomem(subreq, state->ctx)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, ldap_connect_got_tls, state);
+}
+
+static void ldap_connect_got_tls(struct tevent_req *subreq)
+{
+ struct ldap_connect_state *state =
+ tevent_req_callback_data(subreq,
+ struct ldap_connect_state);
+ int err;
+ int ret;
+
+ ret = tstream_tls_connect_recv(subreq, &err, state, &state->tls);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ NTSTATUS status = map_nt_error_from_unix_common(err);
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ talloc_steal(state->tls, state->tls_params);
+
+ state->conn->sockets.raw = talloc_move(state->conn, &state->raw);
+ state->conn->sockets.tls = talloc_move(state->conn->sockets.raw,
+ &state->tls);
+ state->conn->sockets.active = state->conn->sockets.tls;
+ composite_done(state->ctx);
+}
+
+static void ldap_connect_recv_tcp_conn(struct composite_context *ctx)
+{
+ struct ldap_connect_state *state =
+ talloc_get_type_abort(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, &state->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_abort(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_mono(NULL);
+ }
+}
+
+/* Actually this function is NOT ASYNC safe, FIXME? */
+static void ldap_reconnect(struct ldap_connection *conn)
+{
+ NTSTATUS status;
+ time_t now = time_mono(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, status);
+ }
+}
+
+static void ldap_request_destructor_abandon(struct ldap_request *abandon)
+{
+ TALLOC_FREE(abandon);
+}
+
+/* destroy an open ldap request */
+static int ldap_request_destructor(struct ldap_request *req)
+{
+ if (req->state == LDAP_REQUEST_PENDING) {
+ struct ldap_message msg = {
+ .type = LDAP_TAG_AbandonRequest,
+ .r.AbandonRequest.messageid = req->messageid,
+ };
+ struct ldap_request *abandon = NULL;
+
+ DLIST_REMOVE(req->conn->pending, req);
+
+ abandon = ldap_request_send(req->conn, &msg);
+ if (abandon == NULL) {
+ ldap_error_handler(req->conn, NT_STATUS_NO_MEMORY);
+ return 0;
+ }
+ abandon->async.fn = ldap_request_destructor_abandon;
+ abandon->async.private_data = NULL;
+ }
+
+ return 0;
+}
+
+static void ldap_request_timeout_abandon(struct ldap_request *abandon)
+{
+ struct ldap_request *req =
+ talloc_get_type_abort(abandon->async.private_data,
+ struct ldap_request);
+
+ 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 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_abort(private_data,
+ struct ldap_request);
+
+ req->status = NT_STATUS_IO_TIMEOUT;
+ if (req->state == LDAP_REQUEST_PENDING) {
+ struct ldap_message msg = {
+ .type = LDAP_TAG_AbandonRequest,
+ .r.AbandonRequest.messageid = req->messageid,
+ };
+ struct ldap_request *abandon = NULL;
+
+ abandon = ldap_request_send(req->conn, &msg);
+ if (abandon == NULL) {
+ ldap_error_handler(req->conn, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ talloc_reparent(req->conn, req, abandon);
+ abandon->async.fn = ldap_request_timeout_abandon;
+ abandon->async.private_data = req;
+ DLIST_REMOVE(req->conn->pending, req);
+ return;
+ }
+ req->state = LDAP_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+
+/*
+ called on completion of a failed ldap request
+*/
+static void ldap_request_failed_complete(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct ldap_request *req =
+ talloc_get_type_abort(private_data,
+ struct ldap_request);
+
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+static void ldap_request_written(struct tevent_req *subreq);
+
+/*
+ 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;
+ struct tevent_req *subreq = NULL;
+
+ req = talloc_zero(conn, struct ldap_request);
+ if (req == NULL) return NULL;
+
+ if (conn->sockets.active == 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;
+ }
+
+ /* 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);
+ if (req->time_event == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ req->write_iov.iov_base = req->data.data;
+ req->write_iov.iov_len = req->data.length;
+
+ subreq = tstream_writev_queue_send(req, conn->event.event_ctx,
+ conn->sockets.active,
+ conn->sockets.send_queue,
+ &req->write_iov, 1);
+ if (subreq == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ tevent_req_set_callback(subreq, ldap_request_written, req);
+
+ req->state = LDAP_REQUEST_PENDING;
+ DLIST_ADD(conn->pending, req);
+
+ return req;
+
+failed:
+ req->status = status;
+ req->state = LDAP_REQUEST_ERROR;
+ tevent_add_timer(conn->event.event_ctx, req, timeval_zero(),
+ ldap_request_failed_complete, req);
+
+ return req;
+}
+
+static void ldap_request_written(struct tevent_req *subreq)
+{
+ struct ldap_request *req =
+ tevent_req_callback_data(subreq,
+ struct ldap_request);
+ int err;
+ ssize_t ret;
+
+ ret = tstream_writev_queue_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ NTSTATUS error = map_nt_error_from_unix_common(err);
+ ldap_error_handler(req->conn, error);
+ return;
+ }
+
+ if (req->type == LDAP_TAG_AbandonRequest ||
+ req->type == LDAP_TAG_UnbindRequest)
+ {
+ 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);
+ }
+ return;
+ }
+
+ ldap_connection_recv_next(req->conn);
+}
+
+
+/*
+ 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->state = LDAP_REQUEST_ERROR;
+ 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)
+{
+ size_t 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 ((enum ldap_result_code)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) != NULL && (*msg)->type != (enum ldap_request_tag)type) {
+ *msg = NULL;
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ return status;
+}
diff --git a/source4/libcli/ldap/ldap_client.h b/source4/libcli/ldap/ldap_client.h
new file mode 100644
index 0000000..e2b1b30
--- /dev/null
+++ b/source4/libcli/ldap/ldap_client.h
@@ -0,0 +1,149 @@
+/*
+ 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 "system/network.h" /* for struct iovec */
+#include "libcli/ldap/libcli_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 iovec write_iov;
+
+ 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 {
+ struct tstream_context *raw;
+ struct tstream_context *tls;
+ struct tstream_context *sasl;
+ struct tstream_context *active;
+
+ struct tevent_queue *send_queue;
+ struct tevent_req *recv_subreq;
+ } sockets;
+
+ 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;
+ } event;
+};
+
+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 0000000..9193971
--- /dev/null
+++ b/source4/libcli/ldap/ldap_controls.c
@@ -0,0 +1,1283 @@
+/*
+ Unix SMB/CIFS implementation.
+ 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 <ldb.h>
+
+#include "../lib/util/asn1.h"
+#include "libcli/ldap/libcli_ldap.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, ASN1_MAX_TREE_DEPTH);
+ 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, ASN1_MAX_TREE_DEPTH);
+ 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_CONTEXT_SIMPLE(0))) {
+ if (!asn1_read_ContextSimple(data, mem_ctx, 0, &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_CONTEXT_SIMPLE(1))) {
+ bool reverse;
+ if (!asn1_read_BOOLEAN_context(data, &reverse, 1)) {
+ 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, ASN1_MAX_TREE_DEPTH);
+ 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, ASN1_MAX_TREE_DEPTH);
+ 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, (int *) &(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, ASN1_MAX_TREE_DEPTH);
+ 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, (int *) &(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, ASN1_MAX_TREE_DEPTH);
+ 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, ASN1_MAX_TREE_DEPTH);
+ 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, ASN1_MAX_TREE_DEPTH);
+ 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_verify_name_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB name;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ struct ldb_verify_name_control *lvnc;
+ int len;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lvnc = talloc(mem_ctx, struct ldb_verify_name_control);
+ if (!lvnc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvnc->flags))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &name)) {
+ return false;
+ }
+
+ if (name.length) {
+ len = utf16_len_n(name.data, name.length);
+ convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX,
+ name.data, len,
+ (void **)&lvnc->gc, &lvnc->gc_len);
+
+ if (!(lvnc->gc)) {
+ return false;
+ }
+ } else {
+ lvnc->gc_len = 0;
+ lvnc->gc = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lvnc;
+ return true;
+}
+
+static bool encode_verify_name_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_verify_name_control *lvnc = talloc_get_type(in, struct ldb_verify_name_control);
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ DATA_BLOB gc_utf16;
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvnc->flags)) {
+ return false;
+ }
+
+ if (lvnc->gc_len) {
+ convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16,
+ lvnc->gc, lvnc->gc_len,
+ (void **)&gc_utf16.data, &gc_utf16.length);
+ if (!asn1_write_OctetString(data, gc_utf16.data, gc_utf16.length)) {
+ return false;
+ }
+ } else {
+ if (!asn1_write_OctetString(data, NULL, 0)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ 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, ASN1_MAX_TREE_DEPTH);
+ 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_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)) { /*CONTEXT*/
+ return false;
+ }
+
+ } else {
+
+ lvrc->type = 1;
+
+ if (!asn1_read_ContextSimple(data, mem_ctx, 1, &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_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, ASN1_MAX_TREE_DEPTH);
+ 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_memdup(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, ASN1_MAX_TREE_DEPTH);
+
+ 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;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ 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, ASN1_MAX_TREE_DEPTH);
+ int num;
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ /*
+ RFC2891 section 1.1:
+ SortKeyList ::= SEQUENCE OF SEQUENCE {
+ attributeType AttributeDescription,
+ orderingRule [0] MatchingRuleId OPTIONAL,
+ reverseOrder [1] BOOLEAN DEFAULT 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) {
+ DATA_BLOB order = data_blob_string_const(lssc[num]->orderingRule);
+ if (!asn1_write_ContextSimple(data, 0, &order)) {
+ return false;
+ }
+ }
+
+ if (lssc[num]->reverse) {
+ if (!asn1_write_BOOLEAN_context(data, lssc[num]->reverse, 1)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ 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, ASN1_MAX_TREE_DEPTH);
+
+ 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;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ 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, ASN1_MAX_TREE_DEPTH);
+
+ 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;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ 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, ASN1_MAX_TREE_DEPTH);
+
+ 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;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ 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, ASN1_MAX_TREE_DEPTH);
+
+ 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;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ 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, ASN1_MAX_TREE_DEPTH);
+
+ 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;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ 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, ASN1_MAX_TREE_DEPTH);
+
+ 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;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ 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, ASN1_MAX_TREE_DEPTH);
+
+ 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_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)) { /*CONTEXT*/
+ return false;
+ }
+ } else {
+ if (!asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(1))) {
+ return false;
+ }
+
+ if (!asn1_write(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;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ 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, ASN1_MAX_TREE_DEPTH);
+
+ 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;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ 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, ASN1_MAX_TREE_DEPTH);
+
+ 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;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+ }
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ 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, ASN1_MAX_TREE_DEPTH);
+ 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;
+ }
+
+ if (!asn1_read_OctetString_talloc(r[i], data, &r[i]->source_attribute)) {
+ return false;
+ }
+ if (!asn1_read_OctetString_talloc(r[i], data, &r[i]->dereferenced_dn)) {
+ return false;
+ }
+ if (asn1_peek_tag(data, ASN1_CONTEXT(0))) {
+ if (!asn1_start_tag(data, ASN1_CONTEXT(0))) {
+ return false;
+ }
+ if (!ldap_decode_attribs_bare(r, data, &r[i]->attributes,
+ &r[i]->num_attributes)) {
+ return false;
+ }
+ 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 bool encode_flag_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ if (in) {
+ return false;
+ }
+
+ *out = data_blob(NULL, 0);
+ return true;
+}
+
+static bool decode_flag_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ if (in.length != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static const struct ldap_control_handler ldap_known_controls[] = {
+ { LDB_CONTROL_PAGED_RESULTS_OID, decode_paged_results_request, encode_paged_results_request },
+ { LDB_CONTROL_SD_FLAGS_OID, decode_sd_flags_request, encode_sd_flags_request },
+ { LDB_CONTROL_DOMAIN_SCOPE_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_SEARCH_OPTIONS_OID, decode_search_options_request, encode_search_options_request },
+ { LDB_CONTROL_NOTIFICATION_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_TREE_DELETE_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_SHOW_DELETED_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_SHOW_RECYCLED_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_SHOW_DEACTIVATED_LINK_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_EXTENDED_DN_OID, decode_extended_dn_request, encode_extended_dn_request },
+ { LDB_CONTROL_SERVER_SORT_OID, decode_server_sort_request, encode_server_sort_request },
+ { LDB_CONTROL_SORT_RESP_OID, decode_server_sort_response, encode_server_sort_response },
+ { LDB_CONTROL_ASQ_OID, decode_asq_control, encode_asq_control },
+ { LDB_CONTROL_DIRSYNC_OID, decode_dirsync_request, encode_dirsync_request },
+ { LDB_CONTROL_DIRSYNC_EX_OID, decode_dirsync_request, encode_dirsync_request },
+ { LDB_CONTROL_VLV_REQ_OID, decode_vlv_request, encode_vlv_request },
+ { LDB_CONTROL_VLV_RESP_OID, decode_vlv_response, encode_vlv_response },
+ { LDB_CONTROL_PERMISSIVE_MODIFY_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_SERVER_LAZY_COMMIT, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_RODC_DCPROMO_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_RELAX_OID, decode_flag_request, encode_flag_request },
+ { DSDB_OPENLDAP_DEREFERENCE_CONTROL, decode_openldap_dereference, encode_openldap_dereference },
+ { LDB_CONTROL_VERIFY_NAME_OID, decode_verify_name_request, encode_verify_name_request },
+
+ /* the following are internal only, with a network
+ representation */
+ { DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID, decode_flag_request, encode_flag_request },
+
+ /* all the ones below are internal only, and have no network
+ * representation */
+ { DSDB_CONTROL_CURRENT_PARTITION_OID, NULL, NULL },
+ { DSDB_CONTROL_REPLICATED_UPDATE_OID, NULL, NULL },
+ { DSDB_CONTROL_DN_STORAGE_FORMAT_OID, NULL, NULL },
+ { LDB_CONTROL_RECALCULATE_SD_OID, NULL, NULL },
+ { LDB_CONTROL_REVEAL_INTERNALS, NULL, NULL },
+ { LDB_CONTROL_AS_SYSTEM_OID, NULL, NULL },
+ { DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID, NULL, NULL },
+ { DSDB_CONTROL_PASSWORD_HASH_VALUES_OID, NULL, NULL },
+ { DSDB_CONTROL_PASSWORD_CHANGE_OLD_PW_CHECKED_OID, NULL, NULL },
+ { DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID, NULL, NULL },
+ { DSDB_CONTROL_APPLY_LINKS, NULL, NULL },
+ { LDB_CONTROL_BYPASS_OPERATIONAL_OID, NULL, NULL },
+ { DSDB_CONTROL_CHANGEREPLMETADATA_OID, NULL, NULL },
+ { LDB_CONTROL_PROVISION_OID, NULL, NULL },
+ { DSDB_EXTENDED_REPLICATED_OBJECTS_OID, NULL, NULL },
+ { DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID, NULL, NULL },
+ { DSDB_EXTENDED_ALLOCATE_RID_POOL, NULL, NULL },
+ { DSDB_CONTROL_NO_GLOBAL_CATALOG, NULL, NULL },
+ { DSDB_EXTENDED_SCHEMA_UPGRADE_IN_PROGRESS_OID, NULL, NULL },
+ { DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID, NULL, NULL},
+ { 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 0000000..a06e3b6
--- /dev/null
+++ b/source4/libcli/ldap/ldap_ildap.c
@@ -0,0 +1,133 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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/libcli_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_reparent(conn, 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)
+{
+ NTSTATUS status;
+ struct ldb_parse_tree *tree = ldb_parse_tree(conn, expression);
+
+ if (tree == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ 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/ldap/libcli_ldap.h b/source4/libcli/ldap/libcli_ldap.h
new file mode 100644
index 0000000..79cfef2
--- /dev/null
+++ b/source4/libcli/ldap/libcli_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/wscript_build b/source4/libcli/ldap/wscript_build
new file mode 100644
index 0000000..4588233
--- /dev/null
+++ b/source4/libcli/ldap/wscript_build
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+bld.SAMBA_LIBRARY('cli-ldap',
+ source='ldap_client.c ldap_bind.c ldap_ildap.c ldap_controls.c',
+ autoproto='ldap_proto.h',
+ public_deps='samba-errors tevent',
+ private_headers='libcli_ldap.h:ldap-util.h',
+ deps='cli_composite ldb LIBSAMBA_TSOCKET samba_socket NDR_SAMR LIBTLS ndr LP_RESOLVE gensec cli-ldap-common',
+ private_library=True
+ )
+
diff --git a/source4/libcli/libcli.h b/source4/libcli/libcli.h
new file mode 100644
index 0000000..9d2a324
--- /dev/null
+++ b/source4/libcli/libcli.h
@@ -0,0 +1,362 @@
+/*
+ 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"
+#include "libcli/raw/libcliraw.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_options options;
+ struct smbcli_socket *sock; /* NULL if connected */
+ 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;
+
+ssize_t smbcli_read(struct smbcli_tree *tree, int fnum, void *_buf, off_t offset, size_t size);
+
+/****************************************************************************
+ write to a file
+ write_mode: 0x0001 disallow write caching
+ 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);
+
+/****************************************************************************
+ 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);
+
+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,
+ const char *socket_options,
+ struct nbt_name *calling,
+ struct nbt_name *called);
+NTSTATUS smbcli_negprot(struct smbcli_state *cli, bool unicode, int maxprotocol);
+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);
+NTSTATUS smbcli_tconX(struct smbcli_state *cli, const char *sharename,
+ const char *devtype, const char *password);
+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 gensec_settings *gensec_settings);
+NTSTATUS smbcli_tdis(struct smbcli_state *cli);
+
+/****************************************************************************
+ Initialise a client state structure.
+****************************************************************************/
+struct smbcli_state *smbcli_state_init(TALLOC_CTX *mem_ctx);
+bool smbcli_parse_unc(const char *unc_name, TALLOC_CTX *mem_ctx,
+ char **hostname, char **sharename);
+
+/****************************************************************************
+ Symlink a file (UNIX extensions).
+****************************************************************************/
+NTSTATUS smbcli_unix_symlink(struct smbcli_tree *tree, const char *fname_src,
+ const char *fname_dst);
+
+/****************************************************************************
+ Hard a file (UNIX extensions).
+****************************************************************************/
+NTSTATUS smbcli_unix_hardlink(struct smbcli_tree *tree, const char *fname_src,
+ const char *fname_dst);
+
+/****************************************************************************
+ chmod a file (UNIX extensions).
+****************************************************************************/
+NTSTATUS smbcli_unix_chmod(struct smbcli_tree *tree, const char *fname, mode_t mode);
+
+/****************************************************************************
+ chown a file (UNIX extensions).
+****************************************************************************/
+NTSTATUS smbcli_unix_chown(struct smbcli_tree *tree, const char *fname, uid_t uid,
+ gid_t gid);
+
+/****************************************************************************
+ Rename a file.
+****************************************************************************/
+NTSTATUS smbcli_rename(struct smbcli_tree *tree, const char *fname_src,
+ const char *fname_dst);
+
+/****************************************************************************
+ Delete a file.
+****************************************************************************/
+NTSTATUS smbcli_unlink(struct smbcli_tree *tree, const char *fname);
+
+/****************************************************************************
+ Delete a wildcard pattern of files.
+****************************************************************************/
+NTSTATUS smbcli_unlink_wcard(struct smbcli_tree *tree, const char *fname);
+
+/****************************************************************************
+ Create a directory.
+****************************************************************************/
+NTSTATUS smbcli_mkdir(struct smbcli_tree *tree, const char *dname);
+
+/****************************************************************************
+ Remove a directory.
+****************************************************************************/
+NTSTATUS smbcli_rmdir(struct smbcli_tree *tree, const char *dname);
+
+/****************************************************************************
+ Set or clear the delete on close flag.
+****************************************************************************/
+NTSTATUS smbcli_nt_delete_on_close(struct smbcli_tree *tree, int fnum,
+ bool flag);
+
+/****************************************************************************
+ 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);
+
+/****************************************************************************
+ 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);
+
+/****************************************************************************
+ Close a file.
+****************************************************************************/
+NTSTATUS smbcli_close(struct smbcli_tree *tree, int fnum);
+
+/****************************************************************************
+ 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);
+
+/****************************************************************************
+ 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);
+
+/****************************************************************************
+ Unlock a file.
+****************************************************************************/
+NTSTATUS smbcli_unlock(struct smbcli_tree *tree, int fnum, uint32_t offset, uint32_t len);
+
+/****************************************************************************
+ 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);
+
+/****************************************************************************
+ Unlock a file with 64 bit offsets.
+****************************************************************************/
+NTSTATUS smbcli_unlock64(struct smbcli_tree *tree, int fnum, off_t offset,
+ off_t len);
+
+/****************************************************************************
+ 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);
+
+/****************************************************************************
+ Do a SMBgetatr call
+****************************************************************************/
+NTSTATUS smbcli_getatr(struct smbcli_tree *tree, const char *fname,
+ uint16_t *attr, size_t *size, time_t *t);
+
+/****************************************************************************
+ Do a SMBsetatr call.
+****************************************************************************/
+NTSTATUS smbcli_setatr(struct smbcli_tree *tree, const char *fname, uint16_t mode,
+ time_t t);
+
+/****************************************************************************
+ 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);
+
+/****************************************************************************
+ truncate a file to a given size
+****************************************************************************/
+NTSTATUS smbcli_ftruncate(struct smbcli_tree *tree, int fnum, uint64_t size);
+
+/****************************************************************************
+ Check for existence of a dir.
+****************************************************************************/
+NTSTATUS smbcli_chkpath(struct smbcli_tree *tree, const char *path);
+
+/****************************************************************************
+ Query disk space.
+****************************************************************************/
+NTSTATUS smbcli_dskattr(struct smbcli_tree *tree, uint32_t *bsize,
+ uint64_t *total, uint64_t *avail);
+
+/****************************************************************************
+ Create and open a temporary file.
+****************************************************************************/
+int smbcli_ctemp(struct smbcli_tree *tree, const char *path, char **tmp_path);
+
+/****************************************************************************
+ Interpret a long filename structure.
+****************************************************************************/
+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);
+
+/****************************************************************************
+ Interpret a short filename structure.
+ The length of the structure is returned.
+****************************************************************************/
+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);
+
+/****************************************************************************
+ 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);
+
+/****************************************************************************
+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);
+
+/****************************************************************************
+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);
+
+/****************************************************************************
+send a qfileinfo QUERY_FILE_NAME_INFO call
+****************************************************************************/
+NTSTATUS smbcli_qfilename(struct smbcli_tree *tree, int fnum, const char **name);
+
+/****************************************************************************
+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);
+
+/****************************************************************************
+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);
+
+/* The following definitions come from ../source4/libcli/climessage.c */
+
+
+/****************************************************************************
+start a message sequence
+****************************************************************************/
+bool smbcli_message_start(struct smbcli_tree *tree, const char *host, const char *username,
+ int *grp);
+
+/****************************************************************************
+send a message
+****************************************************************************/
+bool smbcli_message_text(struct smbcli_tree *tree, char *msg, int len, int grp);
+
+/****************************************************************************
+end a message
+****************************************************************************/
+bool smbcli_message_end(struct smbcli_tree *tree, int grp);
+
+int smbcli_deltree(struct smbcli_tree *tree, const char *dname);
+
+#endif /* __LIBCLI_H__ */
diff --git a/source4/libcli/rap/rap.c b/source4/libcli/rap/rap.c
new file mode 100644
index 0000000..1b2cadb
--- /dev/null
+++ b/source4/libcli/rap/rap.c
@@ -0,0 +1,1692 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAP client
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Tim Potter 2005
+ Copyright (C) Jelmer Vernooij 2007
+ Copyright (C) Guenther Deschner 2010-2011
+
+ 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 "../librpc/gen_ndr/ndr_rap.h"
+#include "libcli/rap/rap.h"
+#include "librpc/ndr/libndr.h"
+
+struct rap_call *new_rap_cli_call(TALLOC_CTX *mem_ctx, uint16_t callno)
+{
+ struct rap_call *call;
+
+ call = talloc_zero(mem_ctx, struct rap_call);
+ if (call == NULL) {
+ return NULL;
+ }
+
+ call->callno = callno;
+ call->rcv_paramlen = 4;
+
+ call->ndr_push_param = ndr_push_init_ctx(call);
+ if (call->ndr_push_param == NULL) {
+ talloc_free(call);
+ return NULL;
+ }
+ call->ndr_push_param->flags = RAPNDR_FLAGS;
+
+ call->ndr_push_data = ndr_push_init_ctx(call);
+ if (call->ndr_push_data == NULL) {
+ talloc_free(call);
+ return NULL;
+ }
+ call->ndr_push_data->flags = RAPNDR_FLAGS;
+
+ call->pull_mem_ctx = mem_ctx;
+
+ return call;
+}
+
+static void rap_cli_push_paramdesc(struct rap_call *call, char desc)
+{
+ int len = 0;
+
+ if (call->paramdesc != NULL)
+ len = strlen(call->paramdesc);
+
+ call->paramdesc = talloc_realloc(call,
+ call->paramdesc,
+ char,
+ len+2);
+
+ call->paramdesc[len] = desc;
+ call->paramdesc[len+1] = '\0';
+}
+
+static void rap_cli_push_word(struct rap_call *call, uint16_t val)
+{
+ rap_cli_push_paramdesc(call, 'W');
+ ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, val);
+}
+
+static void rap_cli_push_dword(struct rap_call *call, uint32_t val)
+{
+ rap_cli_push_paramdesc(call, 'D');
+ ndr_push_uint32(call->ndr_push_param, NDR_SCALARS, val);
+}
+
+static void rap_cli_push_rcvbuf(struct rap_call *call, int len)
+{
+ rap_cli_push_paramdesc(call, 'r');
+ rap_cli_push_paramdesc(call, 'L');
+ ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, len);
+ call->rcv_datalen = len;
+}
+
+static void rap_cli_push_sendbuf(struct rap_call *call, int len)
+{
+ rap_cli_push_paramdesc(call, 's');
+ rap_cli_push_paramdesc(call, 'T');
+ ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, len);
+}
+
+static void rap_cli_push_param(struct rap_call *call, uint16_t val)
+{
+ rap_cli_push_paramdesc(call, 'P');
+ ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, val);
+}
+
+static void rap_cli_expect_multiple_entries(struct rap_call *call)
+{
+ rap_cli_push_paramdesc(call, 'e');
+ rap_cli_push_paramdesc(call, 'h');
+ call->rcv_paramlen += 4; /* uint16_t entry count, uint16_t total */
+}
+
+static void rap_cli_expect_word(struct rap_call *call)
+{
+ rap_cli_push_paramdesc(call, 'h');
+ call->rcv_paramlen += 2;
+}
+
+static void rap_cli_push_string(struct rap_call *call, const char *str)
+{
+ if (str == NULL) {
+ rap_cli_push_paramdesc(call, 'O');
+ return;
+ }
+ rap_cli_push_paramdesc(call, 'z');
+ ndr_push_string(call->ndr_push_param, NDR_SCALARS, str);
+}
+
+static void rap_cli_expect_format(struct rap_call *call, const char *format)
+{
+ call->datadesc = format;
+}
+
+static void rap_cli_expect_extra_format(struct rap_call *call, const char *format)
+{
+ call->auxdatadesc = format;
+}
+
+static NTSTATUS rap_pull_string(TALLOC_CTX *mem_ctx, struct ndr_pull *ndr,
+ uint16_t convert, const char **dest)
+{
+ uint16_t string_offset;
+ uint16_t ignore;
+ const char *p;
+ size_t len;
+
+ NDR_RETURN(ndr_pull_uint16(ndr, NDR_SCALARS, &string_offset));
+ NDR_RETURN(ndr_pull_uint16(ndr, NDR_SCALARS, &ignore));
+
+ string_offset -= convert;
+
+ if (string_offset+1 > ndr->data_size)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ p = (const char *)(ndr->data + string_offset);
+ len = strnlen(p, ndr->data_size-string_offset);
+
+ if ( string_offset + len + 1 > ndr->data_size )
+ return NT_STATUS_INVALID_PARAMETER;
+
+ *dest = talloc_zero_array(mem_ctx, char, len+1);
+ pull_string(discard_const_p(char, *dest), p, len+1, len, STR_ASCII);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS rap_cli_do_call(struct smbcli_tree *tree,
+ struct rap_call *call)
+{
+ NTSTATUS result;
+ DATA_BLOB param_blob;
+ DATA_BLOB data_blob;
+ struct ndr_push *params;
+ struct ndr_push *data;
+ struct smb_trans2 trans;
+
+ params = ndr_push_init_ctx(call);
+
+ if (params == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ params->flags = RAPNDR_FLAGS;
+
+ data = ndr_push_init_ctx(call);
+
+ if (data == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ data->flags = RAPNDR_FLAGS;
+
+ trans.in.max_param = call->rcv_paramlen;
+ trans.in.max_data = call->rcv_datalen;
+ trans.in.max_setup = 0;
+ trans.in.flags = 0;
+ trans.in.timeout = 0;
+ trans.in.setup_count = 0;
+ trans.in.setup = NULL;
+ trans.in.trans_name = "\\PIPE\\LANMAN";
+
+ NDR_RETURN(ndr_push_uint16(params, NDR_SCALARS, call->callno));
+ if (call->paramdesc)
+ NDR_RETURN(ndr_push_string(params, NDR_SCALARS, call->paramdesc));
+ if (call->datadesc)
+ NDR_RETURN(ndr_push_string(params, NDR_SCALARS, call->datadesc));
+
+ param_blob = ndr_push_blob(call->ndr_push_param);
+ NDR_RETURN(ndr_push_bytes(params, param_blob.data,
+ param_blob.length));
+
+ data_blob = ndr_push_blob(call->ndr_push_data);
+ NDR_RETURN(ndr_push_bytes(data, data_blob.data,
+ data_blob.length));
+
+ if (call->auxdatadesc)
+ NDR_RETURN(ndr_push_string(params, NDR_SCALARS, call->auxdatadesc));
+
+ trans.in.params = ndr_push_blob(params);
+ trans.in.data = ndr_push_blob(data);
+
+ result = smb_raw_trans(tree, call, &trans);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ call->ndr_pull_param = ndr_pull_init_blob(&trans.out.params, call);
+ call->ndr_pull_param->flags = RAPNDR_FLAGS;
+ call->ndr_pull_param->current_mem_ctx = call->pull_mem_ctx;
+ call->ndr_pull_data = ndr_pull_init_blob(&trans.out.data, call);
+ call->ndr_pull_data->flags = RAPNDR_FLAGS;
+ call->ndr_pull_data->current_mem_ctx = call->pull_mem_ctx;
+
+ return result;
+}
+
+
+NTSTATUS smbcli_rap_netshareenum(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetShareEnum *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ int i;
+
+ call = new_rap_cli_call(tree, RAP_WshareEnum);
+
+ if (call == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ rap_cli_push_word(call, r->in.level); /* Level */
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_multiple_entries(call);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "B13");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "B13BWz");
+ break;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetShareEnum, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.count));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ r->out.info = talloc_array(mem_ctx, union rap_share_info, r->out.count);
+
+ if (r->out.info == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<r->out.count; i++) {
+ switch(r->in.level) {
+ case 0:
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ r->out.info[i].info0.share_name, 13));
+ break;
+ case 1:
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ r->out.info[i].info1.share_name, 13));
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ &r->out.info[i].info1.reserved1, 1));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_data,
+ NDR_SCALARS, &r->out.info[i].info1.share_type));
+ RAP_GOTO(rap_pull_string(mem_ctx, call->ndr_pull_data,
+ r->out.convert,
+ &r->out.info[i].info1.comment));
+ break;
+ }
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetShareEnum, r);
+ }
+ result = NT_STATUS_OK;
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netserverenum2(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetServerEnum2 *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ int i;
+
+ call = new_rap_cli_call(mem_ctx, RAP_NetServerEnum2);
+
+ if (call == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_multiple_entries(call);
+ rap_cli_push_dword(call, r->in.servertype);
+ rap_cli_push_string(call, r->in.domain);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "B16");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "B16BBDz");
+ break;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetServerEnum2, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.count));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ r->out.info = talloc_array(mem_ctx, union rap_server_info, r->out.count);
+
+ if (r->out.info == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<r->out.count; i++) {
+ switch(r->in.level) {
+ case 0:
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ r->out.info[i].info0.name, 16));
+ break;
+ case 1:
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ r->out.info[i].info1.name, 16));
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ &r->out.info[i].info1.version_major, 1));
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ &r->out.info[i].info1.version_minor, 1));
+ NDR_GOTO(ndr_pull_uint32(call->ndr_pull_data,
+ NDR_SCALARS, &r->out.info[i].info1.servertype));
+ RAP_GOTO(rap_pull_string(mem_ctx, call->ndr_pull_data,
+ r->out.convert,
+ &r->out.info[i].info1.comment));
+ }
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetServerEnum2, r);
+ }
+
+ result = NT_STATUS_OK;
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netservergetinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_WserverGetInfo *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WserverGetInfo))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_word(call);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "B16");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "B16BBDz");
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_WserverGetInfo, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ switch(r->in.level) {
+ case 0:
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ r->out.info.info0.name, 16));
+ break;
+ case 1:
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ r->out.info.info1.name, 16));
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ &r->out.info.info1.version_major, 1));
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ &r->out.info.info1.version_minor, 1));
+ NDR_GOTO(ndr_pull_uint32(call->ndr_pull_data,
+ NDR_SCALARS, &r->out.info.info1.servertype));
+ RAP_GOTO(rap_pull_string(mem_ctx, call->ndr_pull_data,
+ r->out.convert,
+ &r->out.info.info1.comment));
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_WserverGetInfo, r);
+ }
+ done:
+ talloc_free(call);
+ return result;
+}
+
+static enum ndr_err_code ndr_pull_rap_NetPrintQEnum_data(struct ndr_pull *ndr, struct rap_NetPrintQEnum *r)
+{
+ uint32_t cntr_info_0;
+ TALLOC_CTX *_mem_save_info_0;
+
+ NDR_PULL_ALLOC_N(ndr, r->out.info, r->out.count);
+ _mem_save_info_0 = NDR_PULL_GET_MEM_CTX(ndr);
+ NDR_PULL_SET_MEM_CTX(ndr, r->out.info, 0);
+ for (cntr_info_0 = 0; cntr_info_0 < r->out.count; cntr_info_0++) {
+ NDR_CHECK(ndr_pull_set_switch_value(ndr, &r->out.info[cntr_info_0], r->in.level));
+ NDR_CHECK(ndr_pull_rap_printq_info(ndr, NDR_SCALARS, &r->out.info[cntr_info_0]));
+ }
+ for (cntr_info_0 = 0; cntr_info_0 < r->out.count; cntr_info_0++) {
+ NDR_CHECK(ndr_pull_set_switch_value(ndr, &r->out.info[cntr_info_0], r->in.level));
+ NDR_CHECK(ndr_pull_rap_printq_info(ndr, NDR_BUFFERS, &r->out.info[cntr_info_0]));
+ }
+ NDR_PULL_SET_MEM_CTX(ndr, _mem_save_info_0, 0);
+
+ return NDR_ERR_SUCCESS;
+}
+
+NTSTATUS smbcli_rap_netprintqenum(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintQEnum *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintQEnum))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_multiple_entries(call);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "B13");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "B13BWWWzzzzzWW");
+ break;
+ case 2:
+ rap_cli_expect_format(call, "B13BWWWzzzzzWN");
+ rap_cli_expect_extra_format(call, "WB21BB16B10zWWzDDz");
+ break;
+ case 3:
+ rap_cli_expect_format(call, "zWWWWzzzzWWzzl");
+ break;
+ case 4:
+ rap_cli_expect_format(call, "zWWWWzzzzWNzzl");
+ rap_cli_expect_extra_format(call, "WWzWWDDzz");
+ /* no mention of extra format in MS-RAP */
+ break;
+ case 5:
+ rap_cli_expect_format(call, "z");
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintQEnum, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.count));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ call->ndr_pull_data->relative_rap_convert = r->out.convert;
+
+ NDR_GOTO(ndr_pull_rap_NetPrintQEnum_data(call->ndr_pull_data, r));
+
+ r->out.info = talloc_steal(mem_ctx, r->out.info);
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintQEnum, r);
+ }
+
+ result = NT_STATUS_OK;
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netprintqgetinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintQGetInfo *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintQGetInfo))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_string(call, r->in.PrintQueueName);
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_word(call);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "B13");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "B13BWWWzzzzzWW");
+ break;
+ case 2:
+ rap_cli_expect_format(call, "B13BWWWzzzzzWN");
+ rap_cli_expect_extra_format(call, "WB21BB16B10zWWzDDz");
+ break;
+ case 3:
+ rap_cli_expect_format(call, "zWWWWzzzzWWzzl");
+ break;
+ case 4:
+ rap_cli_expect_format(call, "zWWWWzzzzWNzzl");
+ rap_cli_expect_extra_format(call, "WWzWWDDzz");
+ /* no mention of extra format in MS-RAP */
+ break;
+ case 5:
+ rap_cli_expect_format(call, "z");
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintQGetInfo, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ ZERO_STRUCT(r->out);
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ if (r->out.status == 0) {
+ call->ndr_pull_data->relative_rap_convert = r->out.convert;
+
+ NDR_GOTO(ndr_pull_set_switch_value(call->ndr_pull_data, &r->out.info, r->in.level));
+ NDR_GOTO(ndr_pull_rap_printq_info(call->ndr_pull_data, NDR_SCALARS|NDR_BUFFERS, &r->out.info));
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintQGetInfo, r);
+ }
+
+ result = NT_STATUS_OK;
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netprintjobpause(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintJobPause *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintJobPause))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_word(call, r->in.JobID);
+
+ rap_cli_expect_format(call, "W");
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintJobPause, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintJobPause, r);
+ }
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netprintjobcontinue(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintJobContinue *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintJobContinue))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_word(call, r->in.JobID);
+
+ rap_cli_expect_format(call, "W");
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintJobContinue, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintJobContinue, r);
+ }
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netprintjobdelete(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintJobDelete *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintJobDel))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_word(call, r->in.JobID);
+
+ rap_cli_expect_format(call, "W");
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintJobDelete, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintJobDelete, r);
+ }
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netprintqueuepause(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintQueuePause *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintQPause))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_string(call, r->in.PrintQueueName);
+
+ rap_cli_expect_format(call, "");
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintQueuePause, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintQueuePause, r);
+ }
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netprintqueueresume(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintQueueResume *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintQContinue))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_string(call, r->in.PrintQueueName);
+
+ rap_cli_expect_format(call, "");
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintQueueResume, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintQueueResume, r);
+ }
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netprintqueuepurge(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintQueuePurge *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintQPurge))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_string(call, r->in.PrintQueueName);
+
+ rap_cli_expect_format(call, "");
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintQueuePurge, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintQueuePurge, r);
+ }
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+static enum ndr_err_code ndr_pull_rap_NetPrintJobEnum_data(struct ndr_pull *ndr, struct rap_NetPrintJobEnum *r)
+{
+ uint32_t cntr_info_0;
+ TALLOC_CTX *_mem_save_info_0;
+
+ NDR_PULL_ALLOC_N(ndr, r->out.info, r->out.count);
+ _mem_save_info_0 = NDR_PULL_GET_MEM_CTX(ndr);
+ NDR_PULL_SET_MEM_CTX(ndr, r->out.info, 0);
+ for (cntr_info_0 = 0; cntr_info_0 < r->out.count; cntr_info_0++) {
+ NDR_CHECK(ndr_pull_set_switch_value(ndr, &r->out.info[cntr_info_0], r->in.level));
+ NDR_CHECK(ndr_pull_rap_printj_info(ndr, NDR_SCALARS, &r->out.info[cntr_info_0]));
+ }
+ for (cntr_info_0 = 0; cntr_info_0 < r->out.count; cntr_info_0++) {
+ NDR_CHECK(ndr_pull_set_switch_value(ndr, &r->out.info[cntr_info_0], r->in.level));
+ NDR_CHECK(ndr_pull_rap_printj_info(ndr, NDR_BUFFERS, &r->out.info[cntr_info_0]));
+ }
+ NDR_PULL_SET_MEM_CTX(ndr, _mem_save_info_0, 0);
+
+ return NDR_ERR_SUCCESS;
+}
+
+NTSTATUS smbcli_rap_netprintjobenum(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintJobEnum *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintJobEnum))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_string(call, r->in.PrintQueueName);
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_multiple_entries(call);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "W");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "WB21BB16B10zWWzDDz");
+ break;
+ case 2:
+ rap_cli_expect_format(call, "WWzWWDDzz");
+ break;
+ case 3:
+ rap_cli_expect_format(call, "WWzWWDDzzzzzzzzzzlz");
+ break;
+ case 4:
+ rap_cli_expect_format(call, "WWzWWDDzzzzzDDDDDDD");
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintJobEnum, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.count));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ call->ndr_pull_data->relative_rap_convert = r->out.convert;
+
+ NDR_GOTO(ndr_pull_rap_NetPrintJobEnum_data(call->ndr_pull_data, r));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintJobEnum, r);
+ }
+
+ r->out.info = talloc_steal(mem_ctx, r->out.info);
+
+ result = NT_STATUS_OK;
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netprintjobgetinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintJobGetInfo *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintJobGetInfo))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_word(call, r->in.JobID);
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_word(call);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "W");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "WB21BB16B10zWWzDDz");
+ break;
+ case 2:
+ rap_cli_expect_format(call, "WWzWWDDzz");
+ break;
+ case 3:
+ rap_cli_expect_format(call, "WWzWWDDzzzzzzzzzzlz");
+ break;
+ case 4:
+ rap_cli_expect_format(call, "WWzWWDDzzzzzDDDDDDD");
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintJobGetInfo, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ call->ndr_pull_data->relative_rap_convert = r->out.convert;
+
+ NDR_GOTO(ndr_pull_set_switch_value(call->ndr_pull_data, &r->out.info, r->in.level));
+ NDR_GOTO(ndr_pull_rap_printj_info(call->ndr_pull_data, NDR_SCALARS|NDR_BUFFERS, &r->out.info));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintJobGetInfo, r);
+ }
+
+ result = NT_STATUS_OK;
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netprintjobsetinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintJobSetInfo *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintJobSetInfo))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_word(call, r->in.JobID);
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_sendbuf(call, r->in.bufsize);
+ rap_cli_push_param(call, r->in.ParamNum);
+
+ switch (r->in.ParamNum) {
+ case RAP_PARAM_JOBNUM:
+ case RAP_PARAM_JOBPOSITION:
+ case RAP_PARAM_JOBSTATUS:
+ NDR_GOTO(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r->in.Param.value));
+ break;
+ case RAP_PARAM_USERNAME:
+ case RAP_PARAM_NOTIFYNAME:
+ case RAP_PARAM_DATATYPE:
+ case RAP_PARAM_PARAMETERS_STRING:
+ case RAP_PARAM_JOBSTATUSSTR:
+ case RAP_PARAM_JOBCOMMENT:
+ NDR_GOTO(ndr_push_string(call->ndr_push_param, NDR_SCALARS, r->in.Param.string));
+ break;
+ case RAP_PARAM_TIMESUBMITTED:
+ case RAP_PARAM_JOBSIZE:
+ NDR_GOTO(ndr_push_uint32(call->ndr_push_param, NDR_SCALARS, r->in.Param.value4));
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ /* not really sure if this is correct */
+ rap_cli_expect_format(call, "WB21BB16B10zWWzDDz");
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintJobSetInfo, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+
+ result = NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintJobSetInfo, r);
+ }
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+static enum ndr_err_code ndr_pull_rap_NetPrintDestEnum_data(struct ndr_pull *ndr, struct rap_NetPrintDestEnum *r)
+{
+ uint32_t cntr_info_0;
+ TALLOC_CTX *_mem_save_info_0;
+
+ NDR_PULL_ALLOC_N(ndr, r->out.info, r->out.count);
+ _mem_save_info_0 = NDR_PULL_GET_MEM_CTX(ndr);
+ NDR_PULL_SET_MEM_CTX(ndr, r->out.info, 0);
+ for (cntr_info_0 = 0; cntr_info_0 < r->out.count; cntr_info_0++) {
+ NDR_CHECK(ndr_pull_set_switch_value(ndr, &r->out.info[cntr_info_0], r->in.level));
+ NDR_CHECK(ndr_pull_rap_printdest_info(ndr, NDR_SCALARS, &r->out.info[cntr_info_0]));
+ }
+ for (cntr_info_0 = 0; cntr_info_0 < r->out.count; cntr_info_0++) {
+ NDR_CHECK(ndr_pull_rap_printdest_info(ndr, NDR_BUFFERS, &r->out.info[cntr_info_0]));
+ }
+ NDR_PULL_SET_MEM_CTX(ndr, _mem_save_info_0, 0);
+
+ return NDR_ERR_SUCCESS;
+}
+
+
+NTSTATUS smbcli_rap_netprintdestenum(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintDestEnum *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintDestEnum))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_multiple_entries(call);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "B9");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "B9B21WWzW");
+ break;
+ case 2:
+ rap_cli_expect_format(call, "z");
+ break;
+ case 3:
+ rap_cli_expect_format(call, "zzzWWzzzWW");
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintDestEnum, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.count));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ call->ndr_pull_data->relative_rap_convert = r->out.convert;
+
+ NDR_GOTO(ndr_pull_rap_NetPrintDestEnum_data(call->ndr_pull_data, r));
+
+ r->out.info = talloc_steal(mem_ctx, r->out.info);
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintDestEnum, r);
+ }
+
+ result = NT_STATUS_OK;
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netprintdestgetinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetPrintDestGetInfo *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WPrintDestGetInfo))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_string(call, r->in.PrintDestName);
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_word(call);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "B9");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "B9B21WWzW");
+ break;
+ case 2:
+ rap_cli_expect_format(call, "z");
+ break;
+ case 3:
+ rap_cli_expect_format(call, "zzzWWzzzWW");
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetPrintDestGetInfo, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ call->ndr_pull_data->relative_rap_convert = r->out.convert;
+
+ NDR_GOTO(ndr_pull_set_switch_value(call->ndr_pull_data, &r->out.info, r->in.level));
+ NDR_GOTO(ndr_pull_rap_printdest_info(call->ndr_pull_data, NDR_SCALARS|NDR_BUFFERS, &r->out.info));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetPrintDestGetInfo, r);
+ }
+
+ result = NT_STATUS_OK;
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netuserpasswordset2(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetUserPasswordSet2 *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WUserPasswordSet2))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_string(call, r->in.UserName);
+ rap_cli_push_paramdesc(call, 'b');
+ rap_cli_push_paramdesc(call, '1');
+ rap_cli_push_paramdesc(call, '6');
+ ndr_push_array_uint8(call->ndr_push_param, NDR_SCALARS, r->in.OldPassword, 16);
+ rap_cli_push_paramdesc(call, 'b');
+ rap_cli_push_paramdesc(call, '1');
+ rap_cli_push_paramdesc(call, '6');
+ ndr_push_array_uint8(call->ndr_push_param, NDR_SCALARS, r->in.NewPassword, 16);
+ rap_cli_push_word(call, r->in.EncryptedPassword);
+ rap_cli_push_word(call, r->in.RealPasswordLength);
+
+ rap_cli_expect_format(call, "");
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetUserPasswordSet2, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+
+ result = NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetUserPasswordSet2, r);
+ }
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netoemchangepassword(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetOEMChangePassword *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_SamOEMChgPasswordUser2_P))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_string(call, r->in.UserName);
+ rap_cli_push_sendbuf(call, 532);
+ ndr_push_array_uint8(call->ndr_push_data, NDR_SCALARS, r->in.crypt_password, 516);
+ ndr_push_array_uint8(call->ndr_push_data, NDR_SCALARS, r->in.password_hash, 16);
+
+ rap_cli_expect_format(call, "B516B16");
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetOEMChangePassword, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+
+ result = NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetOEMChangePassword, r);
+ }
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netusergetinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetUserGetInfo *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WUserGetInfo))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_string(call, r->in.UserName);
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_word(call);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "B21");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "B21BB16DWzzWz");
+ break;
+ case 2:
+ rap_cli_expect_format(call, "B21BB16DWzzWzDzzzzDDDDWb21WWzWW");
+ break;
+ case 10:
+ rap_cli_expect_format(call, "B21Bzzz");
+ break;
+ case 11:
+ rap_cli_expect_format(call, "B21BzzzWDDzzDDWWzWzDWb21W");
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetUserGetInfo, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ call->ndr_pull_data->relative_rap_convert = r->out.convert;
+
+ NDR_GOTO(ndr_pull_set_switch_value(call->ndr_pull_data, &r->out.info, r->in.level));
+ NDR_GOTO(ndr_pull_rap_netuser_info(call->ndr_pull_data, NDR_SCALARS|NDR_BUFFERS, &r->out.info));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetUserGetInfo, r);
+ }
+
+ result = NT_STATUS_OK;
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+
+static enum ndr_err_code ndr_pull_rap_NetSessionEnum_data(struct ndr_pull *ndr, struct rap_NetSessionEnum *r)
+{
+ uint32_t cntr_info_0;
+ TALLOC_CTX *_mem_save_info_0;
+
+ NDR_PULL_ALLOC_N(ndr, r->out.info, r->out.count);
+ _mem_save_info_0 = NDR_PULL_GET_MEM_CTX(ndr);
+ NDR_PULL_SET_MEM_CTX(ndr, r->out.info, 0);
+ for (cntr_info_0 = 0; cntr_info_0 < r->out.count; cntr_info_0++) {
+ NDR_CHECK(ndr_pull_set_switch_value(ndr, &r->out.info[cntr_info_0], r->in.level));
+ NDR_CHECK(ndr_pull_rap_session_info(ndr, NDR_SCALARS, &r->out.info[cntr_info_0]));
+ }
+ for (cntr_info_0 = 0; cntr_info_0 < r->out.count; cntr_info_0++) {
+ NDR_CHECK(ndr_pull_set_switch_value(ndr, &r->out.info[cntr_info_0], r->in.level));
+ NDR_CHECK(ndr_pull_rap_session_info(ndr, NDR_BUFFERS, &r->out.info[cntr_info_0]));
+ }
+ NDR_PULL_SET_MEM_CTX(ndr, _mem_save_info_0, 0);
+
+ return NDR_ERR_SUCCESS;
+}
+
+
+NTSTATUS smbcli_rap_netsessionenum(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetSessionEnum *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ call = new_rap_cli_call(tree, RAP_WsessionEnum);
+
+ if (call == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_multiple_entries(call);
+
+ switch(r->in.level) {
+ case 2:
+ rap_cli_expect_format(call, "zzWWWDDDz");
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetSessionEnum, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.count));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ call->ndr_pull_data->relative_rap_convert = r->out.convert;
+
+ NDR_GOTO(ndr_pull_rap_NetSessionEnum_data(call->ndr_pull_data, r));
+
+ r->out.info = talloc_steal(mem_ctx, r->out.info);
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetSessionEnum, r);
+ }
+
+ result = NT_STATUS_OK;
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netsessiongetinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetSessionGetInfo *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WsessionGetInfo))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_string(call, r->in.SessionName);
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_word(call);
+
+ switch(r->in.level) {
+ case 2:
+ rap_cli_expect_format(call, "zzWWWDDDz");
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetSessionGetInfo, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ ZERO_STRUCT(r->out);
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ if (r->out.status == 0 && r->out.available) {
+ call->ndr_pull_data->relative_rap_convert = r->out.convert;
+
+ NDR_GOTO(ndr_pull_set_switch_value(call->ndr_pull_data, &r->out.info, r->in.level));
+ NDR_GOTO(ndr_pull_rap_session_info(call->ndr_pull_data, NDR_SCALARS|NDR_BUFFERS, &r->out.info));
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetSessionGetInfo, r);
+ }
+
+ result = NT_STATUS_OK;
+ done:
+ talloc_free(call);
+ return result;
+}
+
+
+NTSTATUS smbcli_rap_netuseradd(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetUserAdd *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WUserAdd2))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_sendbuf(call, r->in.bufsize);
+ rap_cli_push_word(call, r->in.pwdlength);
+ rap_cli_push_word(call, r->in.unknown);
+
+ switch (r->in.level) {
+ case 1:
+ rap_cli_expect_format(call, "B21BB16DWzzWz");
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetUserAdd, r);
+ }
+
+ NDR_GOTO(ndr_push_set_switch_value(call->ndr_push_data, &r->in.info, r->in.level));
+ NDR_GOTO(ndr_push_rap_netuser_info(call->ndr_push_data, NDR_SCALARS|NDR_BUFFERS, &r->in.info));
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+
+ result = NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetUserAdd, r);
+ }
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netuserdelete(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetUserDelete *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_WUserDel))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_string(call, r->in.UserName);
+
+ rap_cli_expect_format(call, "");
+ rap_cli_expect_extra_format(call, "");
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetUserDelete, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+
+ result = NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetUserDelete, r);
+ }
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+NTSTATUS smbcli_rap_netremotetod(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetRemoteTOD *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, RAP_NetRemoteTOD))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+
+ rap_cli_expect_format(call, "DDBBBBWWBBWB");
+ rap_cli_expect_extra_format(call, "");
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_IN_DEBUG(rap_NetRemoteTOD, r);
+ }
+
+ result = rap_cli_do_call(tree, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_rap_status(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+
+ NDR_GOTO(ndr_pull_rap_TimeOfDayInfo(call->ndr_pull_data, NDR_SCALARS|NDR_BUFFERS, &r->out.tod));
+
+ result = NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_OUT_DEBUG(rap_NetRemoteTOD, r);
+ }
+
+ done:
+ talloc_free(call);
+ return result;
+}
diff --git a/source4/libcli/rap/rap.h b/source4/libcli/rap/rap.h
new file mode 100644
index 0000000..1d44682
--- /dev/null
+++ b/source4/libcli/rap/rap.h
@@ -0,0 +1,76 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAP client
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Tim Potter 2005
+ Copyright (C) Jelmer Vernooij 2007
+ Copyright (C) Guenther Deschner 2010
+
+ 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_GOTO(call) do { \
+ NTSTATUS _status; \
+ _status = call; \
+ if (!NT_STATUS_IS_OK(_status)) { \
+ result = _status; \
+ goto done; \
+ } \
+} while (0)
+
+#define RAP_RETURN(call) do { \
+ NTSTATUS _status; \
+ _status = call; \
+ if (!NT_STATUS_IS_OK(_status)) { \
+ return _status; \
+ } \
+} while (0)
+
+#define NDR_GOTO(call) do { \
+ enum ndr_err_code _ndr_err; \
+ _ndr_err = call; \
+ if (!NDR_ERR_CODE_IS_SUCCESS(_ndr_err)) { \
+ result = ndr_map_error2ntstatus(_ndr_err); \
+ goto done; \
+ } \
+} while (0)
+
+#define NDR_RETURN(call) do { \
+ enum ndr_err_code _ndr_err; \
+ _ndr_err = call; \
+ if (!NDR_ERR_CODE_IS_SUCCESS(_ndr_err)) { \
+ return ndr_map_error2ntstatus(_ndr_err); \
+ } \
+} while (0)
+
+struct rap_call {
+ uint16_t callno;
+ char *paramdesc;
+ const char *datadesc;
+ const char *auxdatadesc;
+
+ uint16_t rcv_paramlen, rcv_datalen;
+
+ struct ndr_push *ndr_push_param;
+ struct ndr_push *ndr_push_data;
+
+ TALLOC_CTX *pull_mem_ctx;
+ struct ndr_pull *ndr_pull_param;
+ struct ndr_pull *ndr_pull_data;
+};
+
+#define RAPNDR_FLAGS (LIBNDR_FLAG_NOALIGN|LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM);
+
+#include "../librpc/gen_ndr/rap.h"
+#include "libcli/rap/proto.h"
diff --git a/source4/libcli/rap/wscript_build b/source4/libcli/rap/wscript_build
new file mode 100644
index 0000000..b58e3ac
--- /dev/null
+++ b/source4/libcli/rap/wscript_build
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_RAP',
+ source='rap.c',
+ public_deps='smbclient-raw NDR_RAP',
+ autoproto='proto.h'
+ )
diff --git a/source4/libcli/raw/README b/source4/libcli/raw/README
new file mode 100644
index 0000000..cb3e507
--- /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 0000000..a417486
--- /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 0000000..6c04843
--- /dev/null
+++ b/source4/libcli/raw/clioplock.c
@@ -0,0 +1,66 @@
+/*
+ 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);
+ if (req == NULL) {
+ return false;
+ }
+
+ 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);
+
+ /*
+ * The low level code knows it is a
+ * one way request by looking at SMBlockingX,
+ * wct == 8 and LOCKING_ANDX_OPLOCK_RELEASE
+ */
+ 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 0000000..5c3d7f9
--- /dev/null
+++ b/source4/libcli/raw/clisession.c
@@ -0,0 +1,310 @@
+/*
+ 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"
+#include "../libcli/smb/smbXcli_base.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 = (uint32_t)getpid();
+ session->vuid = UID_FIELD_INVALID;
+ session->options = options;
+
+ /*
+ * for now session->vuid is still used by the callers, but we call:
+ * smb1cli_session_set_id(session->smbXcli, session->vuid);
+ * before using session->smbXcli, in future we should remove
+ * session->vuid.
+ */
+ session->smbXcli = smbXcli_session_create(session, transport->conn);
+ if (session->smbXcli == NULL) {
+ talloc_free(session);
+ return NULL;
+ }
+
+ 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 (smb1cli_conn_signing_is_active(session->transport->conn)) {
+ 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);
+ 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)) {
+ 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);
+ 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 0000000..1959a93
--- /dev/null
+++ b/source4/libcli/raw/clisocket.c
@@ -0,0 +1,459 @@
+/*
+ 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 "system/network.h"
+#include "../lib/async_req/async_sock.h"
+#include "../lib/util/tevent_ntstatus.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"
+#include "../libcli/smb/read_smb.h"
+
+struct smbcli_transport_connect_state {
+ struct tevent_context *ev;
+ struct socket_context *sock;
+ struct tevent_req *io_req;
+ uint8_t *request;
+ struct iovec iov;
+ uint8_t *response;
+};
+
+static void smbcli_transport_connect_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state);
+static void smbcli_transport_connect_writev_done(struct tevent_req *subreq);
+static void smbcli_transport_connect_read_smb_done(struct tevent_req *subreq);
+
+static struct tevent_req *smbcli_transport_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct socket_context *sock,
+ uint16_t port,
+ uint32_t timeout_msec,
+ struct nbt_name *calling,
+ struct nbt_name *called)
+{
+ struct tevent_req *req;
+ struct smbcli_transport_connect_state *state;
+ struct tevent_req *subreq;
+ DATA_BLOB calling_blob, called_blob;
+ uint8_t *p;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct smbcli_transport_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->sock = sock;
+
+ if (port != 139) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_cleanup_fn(req, smbcli_transport_connect_cleanup);
+
+ status = nbt_name_to_blob(state, &calling_blob, calling);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = nbt_name_to_blob(state, &called_blob, called);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->request = talloc_array(state, uint8_t,
+ NBT_HDR_SIZE +
+ called_blob.length +
+ calling_blob.length);
+ if (tevent_req_nomem(state->request, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /* put in the destination name */
+ p = state->request + 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_nbt(state->request,
+ PTR_DIFF(p, state->request) - NBT_HDR_SIZE);
+ SCVAL(state->request, 0, NBSSrequest);
+
+ state->iov.iov_len = talloc_array_length(state->request);
+ state->iov.iov_base = (void *)state->request;
+
+ subreq = writev_send(state, ev, NULL,
+ sock->fd,
+ true, /* err_on_readability */
+ &state->iov, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ smbcli_transport_connect_writev_done,
+ req);
+ state->io_req = subreq;
+
+ if (timeout_msec > 0) {
+ struct timeval endtime;
+
+ endtime = timeval_current_ofs_msec(timeout_msec);
+ if (!tevent_req_set_endtime(req, ev, endtime)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ return req;
+}
+
+static void smbcli_transport_connect_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
+{
+ struct smbcli_transport_connect_state *state =
+ tevent_req_data(req,
+ struct smbcli_transport_connect_state);
+
+ TALLOC_FREE(state->io_req);
+
+ if (state->sock == NULL) {
+ return;
+ }
+
+ if (state->sock->fd == -1) {
+ return;
+ }
+
+ if (req_state == TEVENT_REQ_DONE) {
+ /*
+ * we keep the socket open for the caller to use
+ */
+ state->sock = NULL;
+ return;
+ }
+
+ close(state->sock->fd);
+ state->sock->fd = -1;
+ state->sock = NULL;
+}
+
+static void smbcli_transport_connect_writev_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct smbcli_transport_connect_state *state =
+ tevent_req_data(req,
+ struct smbcli_transport_connect_state);
+ ssize_t ret;
+ int err;
+
+ state->io_req = NULL;
+
+ ret = writev_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ NTSTATUS status = map_nt_error_from_unix_common(err);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ subreq = read_smb_send(state, state->ev,
+ state->sock->fd);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ smbcli_transport_connect_read_smb_done,
+ req);
+ state->io_req = subreq;
+}
+
+static void smbcli_transport_connect_read_smb_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct smbcli_transport_connect_state *state =
+ tevent_req_data(req,
+ struct smbcli_transport_connect_state);
+ ssize_t ret;
+ int err;
+ NTSTATUS status;
+ uint8_t error;
+
+ state->io_req = NULL;
+
+ ret = read_smb_recv(subreq, state,
+ &state->response, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(err);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (ret < 4) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ switch (CVAL(state->response, 0)) {
+ case NBSSpositive:
+ tevent_req_done(req);
+ return;
+
+ case NBSSnegative:
+ if (ret < 5) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ error = CVAL(state->response, 4);
+ switch (error) {
+ case 0x80:
+ case 0x81:
+ status = NT_STATUS_REMOTE_NOT_LISTENING;
+ break;
+ case 0x82:
+ status = NT_STATUS_RESOURCE_NAME_NOT_FOUND;
+ break;
+ case 0x83:
+ status = NT_STATUS_REMOTE_RESOURCES;
+ break;
+ default:
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ break;
+ }
+ break;
+
+ case NBSSretarget:
+ DEBUG(1,("Warning: session retarget not supported\n"));
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ break;
+ }
+
+ tevent_req_nterror(req, status);
+}
+
+static NTSTATUS smbcli_transport_connect_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+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;
+ struct socket_connect_multi_ex multi_ex;
+ struct nbt_name calling;
+ struct nbt_name called;
+};
+
+/*
+ connect a smbcli_socket context to an IP/port pair
+ if port is 0 then choose 445 then 139
+*/
+
+static struct tevent_req *smbcli_sock_establish_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct socket_context *sock,
+ struct socket_address *addr,
+ void *private_data)
+{
+ struct sock_connect_state *state =
+ talloc_get_type_abort(private_data,
+ struct sock_connect_state);
+ uint32_t timeout_msec = 15 * 1000;
+
+ return smbcli_transport_connect_send(state,
+ ev,
+ sock,
+ addr->port,
+ timeout_msec,
+ &state->calling,
+ &state->called);
+}
+
+static NTSTATUS smbcli_sock_establish_recv(struct tevent_req *req)
+{
+ return smbcli_transport_connect_recv(req);
+}
+
+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 nbt_name *calling,
+ struct nbt_name *called)
+{
+ struct composite_context *result, *ctx;
+ struct sock_connect_state *state;
+ NTSTATUS status;
+ int i;
+
+ result = talloc_zero(mem_ctx, struct composite_context);
+ if (result == NULL) goto failed;
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+
+ result->event_ctx = 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);
+
+ if (!host_addr) {
+ host_addr = host_name;
+ }
+
+ state->multi_ex.private_data = state;
+ state->multi_ex.establish_send = smbcli_sock_establish_send;
+ state->multi_ex.establish_recv = smbcli_sock_establish_recv;
+
+ status = nbt_name_dup(state, calling, &state->calling);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+ status = nbt_name_dup(state, called, &state->called);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ ctx = socket_connect_multi_ex_send(state, host_addr,
+ state->num_ports, state->ports,
+ resolve_ctx,
+ state->ctx->event_ctx,
+ &state->multi_ex);
+ 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_ex_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 = 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 nbt_name *calling,
+ struct nbt_name *called,
+ 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,
+ calling, called);
+ return smbcli_sock_connect_recv(c, mem_ctx, result);
+}
diff --git a/source4/libcli/raw/clitransport.c b/source4/libcli/raw/clitransport.c
new file mode 100644
index 0000000..573e98a
--- /dev/null
+++ b/source4/libcli/raw/clitransport.c
@@ -0,0 +1,673 @@
+/*
+ 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 "system/network.h"
+#include "../lib/async_req/async_sock.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "../libcli/nbt/libnbt.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "../libcli/smb/read_smb.h"
+
+/*
+ destroy a transport
+ */
+static int transport_destructor(struct smbcli_transport *transport)
+{
+ smbcli_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
+ return 0;
+}
+
+/*
+ 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 smbcli_transport *transport;
+ uint32_t smb1_capabilities;
+
+ transport = talloc_zero(parent_ctx, struct smbcli_transport);
+ if (!transport) return NULL;
+
+ transport->ev = sock->event.ctx;
+ transport->options = *options;
+
+ if (transport->options.max_protocol == PROTOCOL_DEFAULT) {
+ transport->options.max_protocol = PROTOCOL_NT1;
+ }
+
+ if (transport->options.max_protocol > PROTOCOL_NT1) {
+ transport->options.max_protocol = PROTOCOL_NT1;
+ }
+
+ TALLOC_FREE(sock->event.fde);
+ TALLOC_FREE(sock->event.te);
+
+ smb1_capabilities = 0;
+ smb1_capabilities |= CAP_LARGE_FILES;
+ smb1_capabilities |= CAP_NT_SMBS | CAP_RPC_REMOTE_APIS;
+ smb1_capabilities |= CAP_LOCK_AND_READ | CAP_NT_FIND;
+ smb1_capabilities |= CAP_DFS | CAP_W2K_SMBS;
+ smb1_capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX;
+ smb1_capabilities |= CAP_LWIO;
+
+ if (options->ntstatus_support) {
+ smb1_capabilities |= CAP_STATUS32;
+ }
+
+ if (options->unicode) {
+ smb1_capabilities |= CAP_UNICODE;
+ }
+
+ if (options->use_spnego) {
+ smb1_capabilities |= CAP_EXTENDED_SECURITY;
+ }
+
+ if (options->use_level2_oplocks) {
+ smb1_capabilities |= CAP_LEVEL_II_OPLOCKS;
+ }
+
+ transport->conn = smbXcli_conn_create(transport,
+ sock->sock->fd,
+ sock->hostname,
+ options->signing,
+ smb1_capabilities,
+ NULL, /* client_guid */
+ 0, /* smb2_capabilities */
+ NULL); /* smb3_ciphers */
+ if (transport->conn == NULL) {
+ TALLOC_FREE(sock);
+ TALLOC_FREE(transport);
+ return NULL;
+ }
+ sock->sock->fd = -1;
+ TALLOC_FREE(sock);
+
+ talloc_set_destructor(transport, transport_destructor);
+
+ return transport;
+}
+
+/*
+ create a transport structure based on an established socket
+*/
+NTSTATUS smbcli_transport_raw_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smbXcli_conn **_conn,
+ const struct smbcli_options *options,
+ struct smbcli_transport **_transport)
+{
+ struct smbcli_transport *transport = NULL;
+ NTSTATUS status;
+
+ if (*_conn == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ transport = talloc_zero(mem_ctx, struct smbcli_transport);
+ if (transport == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ transport->ev = ev;
+ transport->options = *options;
+
+ /*
+ * First only set the pointer without move.
+ */
+ transport->conn = *_conn;
+ status = smb_raw_negotiate_fill_transport(transport);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(transport);
+ return status;
+ }
+
+ talloc_set_destructor(transport, transport_destructor);
+
+ /*
+ * Now move it away from the caller...
+ */
+ transport->conn = talloc_move(transport, _conn);
+ *_transport = transport;
+ return NT_STATUS_OK;
+}
+
+/*
+ mark the transport as dead
+*/
+void smbcli_transport_dead(struct smbcli_transport *transport, NTSTATUS status)
+{
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_LOCAL_DISCONNECT;
+ }
+
+ smbXcli_conn_disconnect(transport->conn, status);
+}
+
+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;
+
+ transport->idle.func(transport, transport->idle.private_data);
+
+ if (transport->idle.func == NULL) {
+ return;
+ }
+
+ if (!smbXcli_conn_is_connected(transport->conn)) {
+ return;
+ }
+
+ next = timeval_current_ofs_usec(transport->idle.period);
+
+ transport->idle.te = tevent_add_timer(transport->ev,
+ transport,
+ next,
+ idle_handler,
+ transport);
+}
+
+/*
+ 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)
+{
+ TALLOC_FREE(transport->idle.te);
+ ZERO_STRUCT(transport->idle);
+
+ if (idle_func == NULL) {
+ return;
+ }
+
+ if (!smbXcli_conn_is_connected(transport->conn)) {
+ return;
+ }
+
+ transport->idle.func = idle_func;
+ transport->idle.private_data = private_data;
+ transport->idle.period = period;
+
+ transport->idle.te = tevent_add_timer(transport->ev,
+ transport,
+ timeval_current_ofs_usec(period),
+ idle_handler,
+ transport);
+}
+
+/*
+ process some read/write requests that are pending
+ return false if the socket is dead
+*/
+_PUBLIC_ bool smbcli_transport_process(struct smbcli_transport *transport)
+{
+ struct tevent_req *subreq = NULL;
+ int ret;
+
+ if (!smbXcli_conn_is_connected(transport->conn)) {
+ return false;
+ }
+
+ if (!smbXcli_conn_has_async_calls(transport->conn)) {
+ return true;
+ }
+
+ /*
+ * do not block for more than 500 micro seconds
+ */
+ subreq = tevent_wakeup_send(transport,
+ transport->ev,
+ timeval_current_ofs_usec(500));
+ if (subreq == NULL) {
+ return false;
+ }
+
+ ret = tevent_loop_once(transport->ev);
+ if (ret != 0) {
+ return false;
+ }
+
+ TALLOC_FREE(subreq);
+
+ if (!smbXcli_conn_is_connected(transport->conn)) {
+ return false;
+ }
+
+ return true;
+}
+
+static void smbcli_transport_break_handler(struct tevent_req *subreq);
+static void smbcli_request_done(struct tevent_req *subreq);
+
+struct tevent_req *smbcli_transport_setup_subreq(struct smbcli_request *req)
+{
+ struct smbcli_transport *transport = req->transport;
+ uint8_t smb_command;
+ uint8_t additional_flags;
+ uint8_t clear_flags;
+ uint16_t additional_flags2;
+ uint16_t clear_flags2;
+ uint32_t pid;
+ struct smbXcli_tcon *tcon = NULL;
+ struct smbXcli_session *session = NULL;
+ uint32_t timeout_msec = transport->options.request_timeout * 1000;
+ struct iovec *bytes_iov = NULL;
+ struct tevent_req *subreq = NULL;
+
+ smb_command = SVAL(req->out.hdr, HDR_COM);
+ additional_flags = CVAL(req->out.hdr, HDR_FLG);
+ additional_flags2 = SVAL(req->out.hdr, HDR_FLG2);
+ pid = SVAL(req->out.hdr, HDR_PID);
+ pid |= SVAL(req->out.hdr, HDR_PIDHIGH)<<16;
+
+ clear_flags = ~additional_flags;
+ clear_flags2 = ~additional_flags2;
+
+ if (req->session) {
+ session = req->session->smbXcli;
+ }
+
+ if (req->tree) {
+ tcon = req->tree->smbXcli;
+ }
+
+ bytes_iov = talloc(req, struct iovec);
+ if (bytes_iov == NULL) {
+ return NULL;
+ }
+ bytes_iov->iov_base = (void *)req->out.data;
+ bytes_iov->iov_len = req->out.data_size;
+
+ subreq = smb1cli_req_create(req,
+ transport->ev,
+ transport->conn,
+ smb_command,
+ additional_flags,
+ clear_flags,
+ additional_flags2,
+ clear_flags2,
+ timeout_msec,
+ pid,
+ tcon,
+ session,
+ req->out.wct,
+ (uint16_t *)req->out.vwv,
+ 1, bytes_iov);
+ if (subreq == NULL) {
+ return NULL;
+ }
+
+ ZERO_STRUCT(req->out);
+
+ return subreq;
+}
+
+/*
+ put a request into the send queue
+*/
+void smbcli_transport_send(struct smbcli_request *req)
+{
+ struct smbcli_transport *transport = req->transport;
+ NTSTATUS status;
+ bool need_pending_break = false;
+ struct tevent_req *subreq = NULL;
+ size_t i;
+ size_t num_subreqs = 0;
+
+ if (transport->oplock.handler) {
+ need_pending_break = true;
+ }
+
+ if (transport->break_subreq) {
+ need_pending_break = false;
+ }
+
+ if (need_pending_break) {
+ subreq = smb1cli_req_create(transport,
+ transport->ev,
+ transport->conn,
+ 0, /* smb_command */
+ 0, /* additional_flags */
+ 0, /* clear_flags */
+ 0, /* additional_flags2 */
+ 0, /* clear_flags2 */
+ 0, /* timeout_msec */
+ 0, /* pid */
+ NULL, /* tcon */
+ NULL, /* session */
+ 0, /* wct */
+ NULL, /* vwv */
+ 0, /* iov_count */
+ NULL); /* bytes_iov */
+ if (subreq != NULL) {
+ smb1cli_req_set_mid(subreq, 0xFFFF);
+ smbXcli_req_set_pending(subreq);
+ tevent_req_set_callback(subreq,
+ smbcli_transport_break_handler,
+ transport);
+ transport->break_subreq = subreq;
+ subreq = NULL;
+ }
+ }
+
+ subreq = smbcli_transport_setup_subreq(req);
+ if (subreq == NULL) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ req->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(req->subreqs); i++) {
+ if (req->subreqs[i] == NULL) {
+ req->subreqs[i] = subreq;
+ subreq = NULL;
+ }
+ if (req->subreqs[i] == NULL) {
+ break;
+ }
+
+ if (!tevent_req_is_in_progress(req->subreqs[i])) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ return;
+ }
+ }
+ num_subreqs = i;
+
+ req->state = SMBCLI_REQUEST_RECV;
+ tevent_req_set_callback(req->subreqs[0], smbcli_request_done, req);
+
+ status = smb1cli_req_chain_submit(req->subreqs, num_subreqs);
+ if (!NT_STATUS_IS_OK(status)) {
+ req->status = status;
+ req->state = SMBCLI_REQUEST_ERROR;
+ smbXcli_conn_disconnect(transport->conn, status);
+ }
+}
+
+static void smbcli_request_done(struct tevent_req *subreq)
+{
+ struct smbcli_request *req =
+ tevent_req_callback_data(subreq,
+ struct smbcli_request);
+ struct smbcli_transport *transport = req->transport;
+ ssize_t len;
+ size_t i;
+ uint8_t *hdr = NULL;
+ uint8_t wct = 0;
+ uint16_t *vwv = NULL;
+ uint32_t num_bytes = 0;
+ uint8_t *bytes = NULL;
+ struct iovec *recv_iov = NULL;
+ uint8_t *inbuf = NULL;
+
+ req->status = smb1cli_req_recv(req->subreqs[0], req,
+ &recv_iov,
+ &hdr,
+ &wct,
+ &vwv,
+ NULL, /* pvwv_offset */
+ &num_bytes,
+ &bytes,
+ NULL, /* pbytes_offset */
+ &inbuf,
+ NULL, 0); /* expected */
+ TALLOC_FREE(req->subreqs[0]);
+ if (!NT_STATUS_IS_OK(req->status)) {
+ if (recv_iov == NULL) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ transport->error.e.nt_status = req->status;
+ transport->error.etype = ETYPE_SOCKET;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+ }
+
+ /*
+ * For SMBreadBraw hdr is NULL
+ */
+ len = recv_iov[0].iov_len;
+ for (i=1; hdr != NULL && i < 3; i++) {
+ uint8_t *p = recv_iov[i-1].iov_base;
+ uint8_t *c1 = recv_iov[i].iov_base;
+ uint8_t *c2 = p + recv_iov[i-1].iov_len;
+
+ len += recv_iov[i].iov_len;
+
+ c2 += i;
+ len += i;
+
+ if (recv_iov[i].iov_len == 0) {
+ continue;
+ }
+
+ if (c1 != c2) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ transport->error.e.nt_status = req->status;
+ transport->error.etype = ETYPE_SMB;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+ }
+
+ /* fill in the 'in' portion of the matching request */
+ req->in.buffer = inbuf;
+ req->in.size = NBT_HDR_SIZE + len;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = hdr;
+ req->in.vwv = (uint8_t *)vwv;
+ req->in.wct = wct;
+ req->in.data = bytes;
+ req->in.data_size = num_bytes;
+ req->in.ptr = req->in.data;
+ if (hdr != NULL) {
+ req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
+ }
+
+ smb_setup_bufinfo(req);
+
+ transport->error.e.nt_status = req->status;
+ if (NT_STATUS_IS_OK(req->status)) {
+ transport->error.etype = ETYPE_NONE;
+ } else {
+ transport->error.etype = ETYPE_SMB;
+ }
+
+ req->state = SMBCLI_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+static void smbcli_transport_break_handler(struct tevent_req *subreq)
+{
+ struct smbcli_transport *transport =
+ tevent_req_callback_data(subreq,
+ struct smbcli_transport);
+ NTSTATUS status;
+ struct iovec *recv_iov = NULL;
+ uint8_t *hdr = NULL;
+ uint16_t *vwv = NULL;
+ const struct smb1cli_req_expected_response expected[] = {
+ {
+ .status = NT_STATUS_OK,
+ .wct = 8,
+ }
+ };
+ uint16_t tid;
+ uint16_t fnum;
+ uint8_t level;
+
+ transport->break_subreq = NULL;
+
+ status = smb1cli_req_recv(subreq, transport,
+ &recv_iov,
+ &hdr,
+ NULL, /* pwct */
+ &vwv,
+ NULL, /* pvwv_offset */
+ NULL, /* pnum_bytes */
+ NULL, /* pbytes */
+ NULL, /* pbytes_offset */
+ NULL, /* pinbuf */
+ expected,
+ ARRAY_SIZE(expected));
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(recv_iov);
+ smbcli_transport_dead(transport, status);
+ return;
+ }
+
+ /*
+ * Setup the subreq to handle the
+ * next incoming SMB2 Break.
+ */
+ subreq = smb1cli_req_create(transport,
+ transport->ev,
+ transport->conn,
+ 0, /* smb_command */
+ 0, /* additional_flags */
+ 0, /* clear_flags */
+ 0, /* additional_flags2 */
+ 0, /* clear_flags2 */
+ 0, /* timeout_msec */
+ 0, /* pid */
+ NULL, /* tcon */
+ NULL, /* session */
+ 0, /* wct */
+ NULL, /* vwv */
+ 0, /* iov_count */
+ NULL); /* bytes_iov */
+ if (subreq != NULL) {
+ smb1cli_req_set_mid(subreq, 0xFFFF);
+ smbXcli_req_set_pending(subreq);
+ tevent_req_set_callback(subreq,
+ smbcli_transport_break_handler,
+ transport);
+ transport->break_subreq = subreq;
+ }
+
+ tid = SVAL(hdr, HDR_TID);
+ fnum = SVAL(vwv+2, 0);
+ level = CVAL(vwv+3, 1);
+
+ TALLOC_FREE(recv_iov);
+
+ if (transport->oplock.handler) {
+ transport->oplock.handler(transport, tid, fnum, level,
+ transport->oplock.private_data);
+ } else {
+ DEBUG(5,("Got SMB oplock break with no handler\n"));
+ }
+
+}
+
+
+/****************************************************************************
+ 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 0000000..e6b7c8a
--- /dev/null
+++ b/source4/libcli/raw/clitree.c
@@ -0,0 +1,228 @@
+/*
+ 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"
+#include "../libcli/smb/smbXcli_base.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);
+ }
+
+ tree->smbXcli = smbXcli_tcon_create(tree);
+ if (tree->smbXcli == NULL) {
+ talloc_free(tree);
+ return NULL;
+ }
+
+ 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 >= 3) {
+ parms->tconx.out.options = SVAL(req->in.vwv, VWV(2));
+ }
+ if (req->in.wct >= 7) {
+ parms->tconx.out.max_access = IVAL(req->in.vwv, VWV(3));
+ parms->tconx.out.guest_max_access = IVAL(req->in.vwv, VWV(5));
+ }
+
+ /* 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);
+ 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 (req == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ 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 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.existing_conn = NULL;
+ 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;
+
+ 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 0000000..5b398ee
--- /dev/null
+++ b/source4/libcli/raw/interfaces.h
@@ -0,0 +1,2867 @@
+/*
+ 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 "source4/libcli/raw/smb.h"
+#include "../libcli/smb/smb_common.h"
+#include "librpc/gen_ndr/misc.h" /* for struct GUID */
+#include "librpc/gen_ndr/smb2_lease_struct.h"
+
+/* 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 smb2_lease_break {
+ struct smb2_lease current_lease;
+ uint16_t new_epoch; /* only for v2 leases */
+ uint32_t break_flags;
+ uint32_t new_lease_state;
+ uint32_t break_reason; /* should be 0 */
+ uint32_t access_mask_hint; /* should be 0 */
+ uint32_t share_mask_hint; /* should be 0 */
+};
+
+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;
+ unsigned int 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;
+ uint32_t max_access;
+ uint32_t guest_max_access;
+ 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,
+ RAW_FILEINFO_NORMALIZED_NAME_INFORMATION= SMB_QFILEINFO_NORMALIZED_NAME_INFORMATION,
+ /* SMB2 specific levels */
+ RAW_FILEINFO_SMB2_ALL_EAS = 0x0f01,
+ RAW_FILEINFO_SMB2_ALL_INFORMATION = 0x1201,
+ RAW_FILEINFO_SMB2_ALT_NAME_INFORMATION = 0x1501
+};
+
+/* 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;
+ unsigned int 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;
+ unsigned int 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;
+ unsigned int num_names;
+ struct ea_name {
+ struct smb_wire_string name;
+ } *ea_names;
+ } in;
+
+ struct smb_ea_list {
+ unsigned int 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 */
+ /* RAW_FILEINFO_ALT_NAME_INFO and RAW_FILEINFO_ALT_NAME_INFORMATION interfaces */
+ /* RAW_FILEINFO_NORMALIZED_NAME_INFORMATION interface */
+ /* RAW_FILEINFO_SMB2_ALT_NAME_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ struct smb_wire_string fname;
+ } out;
+ } name_info, alt_name_info, normalized_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_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 {
+ unsigned int 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_SET_FILE_UNIX_LINK,
+ RAW_SFILEINFO_UNIX_HLINK = SMB_SET_FILE_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;
+ unsigned int 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_SFILEINFO_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,
+ RAW_QFS_SECTOR_SIZE_INFORMATION = SMB_QFS_SECTOR_SIZE_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;
+
+ /* trans2 RAW_QFS_SECTOR_SIZE_INFORMATION interface */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint32_t logical_bytes_per_sector;
+ uint32_t phys_bytes_per_sector_atomic;
+ uint32_t phys_bytes_per_sector_perf;
+ uint32_t fs_effective_phys_bytes_per_sector_atomic;
+ uint32_t flags;
+ uint32_t byte_off_sector_align;
+ uint32_t byte_off_partition_align;
+ } out;
+ } sector_size_info;
+};
+
+
+enum smb_setfsinfo_level {
+ RAW_SETFS_UNIX_INFO = SMB_SET_CIFS_UNIX_INFO};
+
+union smb_setfsinfo {
+ /* generic interface */
+ struct {
+ enum smb_setfsinfo_level level;
+ } generic;
+
+ /* TRANS2 RAW_QFS_UNIX_INFO interface */
+ struct {
+ enum smb_setfsinfo_level level;
+
+ struct {
+ uint16_t major_version;
+ uint16_t minor_version;
+ uint64_t capability;
+ } in;
+ } unix_info;
+};
+
+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_NTCREATEX_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_NTCREATEX_READX: \
+ file = &op->ntcreatexreadx.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;
+ union smb_handle 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;
+ bool query_on_disk_id;
+
+ /* private flags for internal use only */
+ uint8_t private_flags;
+ } 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;
+ uint8_t on_disk_id[32];
+ } 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;
+ unsigned int 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;
+
+ /* chained NTCreateX/ReadX interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint32_t flags;
+ union smb_handle 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;
+
+ /* readx part */
+ uint64_t offset;
+ uint16_t mincnt;
+ uint32_t maxcnt;
+ uint16_t remaining;
+ } 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;
+
+ /* readx part */
+ uint8_t *data;
+ uint16_t remaining;
+ uint16_t compaction_mode;
+ uint16_t nread;
+ } out;
+ } ntcreatexreadx;
+
+#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;
+
+ /* data for durable handle v2 */
+ bool durable_open_v2;
+ struct GUID create_guid;
+ bool persistent_open;
+ uint32_t timeout;
+ struct smb2_handle *durable_handle_v2;
+
+ bool query_maximal_access;
+ NTTIME timewarp;
+ bool query_on_disk_id;
+ struct smb2_lease *lease_request;
+ struct smb2_lease *lease_request_v2;
+
+ struct GUID *app_instance_id;
+
+ /* and any additional blobs the caller wants */
+ struct smb2_create_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_status;
+ uint32_t maximal_access;
+ uint8_t on_disk_id[32];
+ struct smb2_lease lease_response;
+ struct smb2_lease lease_response_v2;
+ bool durable_open;
+
+ /* durable handle v2 */
+ bool durable_open_v2;
+ bool persistent_open;
+ uint32_t timeout;
+
+ /* 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;
+ uint16_t flags2;
+ uint16_t data_offset;
+ } 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 lock_sequence;
+ /* struct smb2_handle handle; */
+ struct smb2_lock_element *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;
+
+ /* SMB2 Lease Break Ack (same opcode as smb2_break) */
+ struct smb2_lease_break_ack {
+ struct {
+ uint32_t reserved;
+ struct smb2_lease lease;
+ } in, out;
+ } smb2_lease_break_ack;
+};
+
+
+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;
+ 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 reserved;
+ uint32_t function;
+ /*struct smb2_handle handle;*/
+ /* uint32_t out_ofs; */
+ /* uint32_t out_size; */
+ uint32_t max_input_response;
+ /* uint32_t in_ofs; */
+ /* uint32_t in_size; */
+ uint32_t max_output_response;
+ uint32_t flags;
+ uint32_t reserved2;
+
+ /* 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 reserved;
+ 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 flags;
+ uint32_t reserved2;
+
+ /* 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 */
+ unsigned int num_names;
+ struct ea_name *ea_names;
+ } in;
+ struct {
+ uint16_t handle;
+ uint16_t count;
+ uint16_t end_of_search;
+ } out;
+ } t2ffirst;
+
+ /* 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 */
+ unsigned int 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;
+ uint8_t short_name_buf[24];
+ 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/libcliraw.h b/source4/libcli/raw/libcliraw.h
new file mode 100644
index 0000000..a4c4cf3
--- /dev/null
+++ b/source4/libcli/raw/libcliraw.h
@@ -0,0 +1,343 @@
+/*
+ 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/smb/smb_common.h"
+#include "libcli/raw/request.h"
+#include "librpc/gen_ndr/nbt.h"
+#include "libcli/raw/interfaces.h"
+#include "libcli/smb/smb2_negotiate_context.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 */
+ DATA_BLOB secblob; /* cryptkey or negTokenInit blob */
+ uint32_t sesskey;
+
+ /* capabilities that the server reported */
+ uint32_t capabilities;
+
+ int server_zone;
+ time_t server_time;
+
+ unsigned int readbraw_supported:1;
+ unsigned int writebraw_supported:1;
+ unsigned int lockread_supported:1;
+};
+
+/* 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 {
+ unsigned int use_oplocks:1;
+ unsigned int use_level2_oplocks:1;
+ unsigned int use_spnego:1;
+ unsigned int unicode:1;
+ unsigned int ntstatus_support:1;
+ int min_protocol;
+ int max_protocol;
+ uint32_t max_xmit;
+ uint16_t max_mux;
+ int request_timeout;
+ enum smb_signing_setting signing;
+ uint32_t smb2_capabilities;
+ struct GUID client_guid;
+ uint64_t max_credits;
+ unsigned int only_negprot;
+ struct smb311_capabilities smb3_capabilities;
+};
+
+/* this is the context for the client transport layer */
+struct smbcli_transport {
+ struct tevent_context *ev; /* TODO: remove this !!! */
+ struct smbXcli_conn *conn;
+
+ /* negotiated protocol information */
+ struct smbcli_negotiate negotiate;
+
+ /* options to control the behaviour of the client code */
+ struct smbcli_options options;
+
+ /* 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;
+ unsigned int period;
+ struct tevent_timer *te;
+ } 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;
+ unsigned int 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;
+ struct tevent_req *break_subreq;
+};
+
+/* 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 */
+ struct smbXcli_session *smbXcli;
+ 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;
+
+ /* the spnego context if we use extented security */
+ struct gensec_security *gensec;
+
+ struct smbcli_session_options {
+ unsigned int lanman_auth:1;
+ unsigned int ntlmv2_auth:1;
+ unsigned int plaintext_auth:1;
+ } options;
+
+ const char *os;
+ const char *lanman;
+};
+
+/*
+ smbcli_tree context: internal state for a tree connection.
+ */
+struct smbcli_tree {
+ /* session layer info */
+ struct smbcli_session *session;
+
+ struct smbXcli_tcon *smbXcli;
+ 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 {
+ /* smbXcli_req */
+ struct tevent_req *subreqs[2];
+
+ /* 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;
+
+ /* 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 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;
+
+ struct smb_request_buffer in;
+ struct smb_request_buffer out;
+
+ struct smb_trans2 trans2;
+ struct smb_nttrans nttrans;
+
+ /* 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; \
+}
+
+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);
+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);
+NTSTATUS smb_raw_write_recv(struct smbcli_request *req, 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_setfsinfo(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_setfsinfo *set_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);
+
+#endif /* __LIBCLI_RAW__H__ */
diff --git a/source4/libcli/raw/rawacl.c b/source4/libcli/raw/rawacl.c
new file mode 100644
index 0000000..c2de2cc
--- /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);
+ 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);
+ 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 0000000..5264653
--- /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 0000000..2f551b0
--- /dev/null
+++ b/source4/libcli/raw/raweas.c
@@ -0,0 +1,371 @@
+/*
+ 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 "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(unsigned int num_eas, struct ea_struct *eas)
+{
+ unsigned int 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 unsigned int ea_name_list_size(unsigned int num_names, struct ea_name *eas)
+{
+ unsigned int 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(unsigned int num_eas, struct ea_struct *eas, unsigned alignment)
+{
+ unsigned int total = 0;
+ int i;
+ for (i=0;i<num_eas;i++) {
+ unsigned int 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, unsigned int 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++) {
+ unsigned int 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);
+ if (eas[i].value.length > 0) {
+ 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, unsigned int num_eas, struct ea_struct *eas,
+ unsigned alignment)
+{
+ int i;
+
+ for (i=0;i<num_eas;i++) {
+ unsigned int nlen = strlen(eas[i].name.s);
+ uint32_t len = 8+nlen+1+eas[i].value.length;
+ unsigned int 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
+*/
+unsigned int 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,
+ unsigned int *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) {
+ unsigned int 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,
+ unsigned int *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) {
+ unsigned int 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;
+ }
+
+ if (ofs + next_ofs < ofs) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ofs += next_ofs;
+ if (ofs+4 > blob->length || ofs+4 < ofs) {
+ 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 unsigned int 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,
+ unsigned int *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) {
+ unsigned int 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, unsigned int 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++) {
+ unsigned int 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 0000000..39e9a8d
--- /dev/null
+++ b/source4/libcli/raw/rawfile.c
@@ -0,0 +1,1052 @@
+/*
+ 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 "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)
+
+/****************************************************************************
+ 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,
+ 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.fnum);
+ 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);
+ if (sd_blob.length > 0) {
+ memcpy(nt.in.data.data, sd_blob.data, sd_blob.length);
+ }
+ if (ea_blob.length > 0) {
+ 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.fnum);
+ 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_NTCREATEX_READX:
+ 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->ntcreatexreadx.in.flags);
+ SIVAL(req->out.vwv, 11, parms->ntcreatexreadx.in.root_fid.fnum);
+ SIVAL(req->out.vwv, 15, parms->ntcreatexreadx.in.access_mask);
+ SBVAL(req->out.vwv, 19, parms->ntcreatexreadx.in.alloc_size);
+ SIVAL(req->out.vwv, 27, parms->ntcreatexreadx.in.file_attr);
+ SIVAL(req->out.vwv, 31, parms->ntcreatexreadx.in.share_access);
+ SIVAL(req->out.vwv, 35, parms->ntcreatexreadx.in.open_disposition);
+ SIVAL(req->out.vwv, 39, parms->ntcreatexreadx.in.create_options);
+ SIVAL(req->out.vwv, 43, parms->ntcreatexreadx.in.impersonation);
+ SCVAL(req->out.vwv, 47, parms->ntcreatexreadx.in.security_flags);
+
+ smbcli_req_append_string_len(req, parms->ntcreatexreadx.in.fname, STR_TERMINATE, &len);
+ SSVAL(req->out.vwv, 5, len);
+
+ 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->ntcreatexreadx.in.offset);
+ SSVAL(req->out.vwv, VWV(5), parms->ntcreatexreadx.in.maxcnt & 0xFFFF);
+ SSVAL(req->out.vwv, VWV(6), parms->ntcreatexreadx.in.mincnt);
+ SIVAL(req->out.vwv, VWV(7), parms->ntcreatexreadx.in.maxcnt >> 16);
+ SSVAL(req->out.vwv, VWV(9), parms->ntcreatexreadx.in.remaining);
+ if (bigoffset) {
+ SIVAL(req->out.vwv, VWV(10),parms->ntcreatexreadx.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_NTCREATEX_READX:
+ SMBCLI_CHECK_MIN_WCT(req, 34);
+ parms->ntcreatexreadx.out.oplock_level = CVAL(req->in.vwv, 4);
+ parms->ntcreatexreadx.out.file.fnum = SVAL(req->in.vwv, 5);
+ parms->ntcreatexreadx.out.create_action = IVAL(req->in.vwv, 7);
+ parms->ntcreatexreadx.out.create_time = smbcli_pull_nttime(req->in.vwv, 11);
+ parms->ntcreatexreadx.out.access_time = smbcli_pull_nttime(req->in.vwv, 19);
+ parms->ntcreatexreadx.out.write_time = smbcli_pull_nttime(req->in.vwv, 27);
+ parms->ntcreatexreadx.out.change_time = smbcli_pull_nttime(req->in.vwv, 35);
+ parms->ntcreatexreadx.out.attrib = IVAL(req->in.vwv, 43);
+ parms->ntcreatexreadx.out.alloc_size = BVAL(req->in.vwv, 47);
+ parms->ntcreatexreadx.out.size = BVAL(req->in.vwv, 55);
+ parms->ntcreatexreadx.out.file_type = SVAL(req->in.vwv, 63);
+ parms->ntcreatexreadx.out.ipc_state = SVAL(req->in.vwv, 65);
+ parms->ntcreatexreadx.out.is_directory = CVAL(req->in.vwv, 67);
+
+ status = smbcli_chained_advance(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ SMBCLI_CHECK_WCT(req, 12);
+ parms->ntcreatexreadx.out.remaining = SVAL(req->in.vwv, VWV(2));
+ parms->ntcreatexreadx.out.compaction_mode = SVAL(req->in.vwv, VWV(3));
+ parms->ntcreatexreadx.out.nread = SVAL(req->in.vwv, VWV(5));
+ if (parms->ntcreatexreadx.out.nread >
+ MAX(parms->ntcreatexreadx.in.mincnt, parms->ntcreatexreadx.in.maxcnt) ||
+ !smbcli_raw_pull_data(&req->in.bufinfo, req->in.hdr + SVAL(req->in.vwv, VWV(6)),
+ parms->ntcreatexreadx.out.nread,
+ parms->ntcreatexreadx.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:
+ case RAW_CLOSE_GENERIC:
+ 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;
+ unsigned int lck_size = (parms->lockx.in.mode & LOCKING_ANDX_LARGE_FILES)? 20 : 10;
+ unsigned int 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:
+ case RAW_LOCK_SMB2_BREAK:
+ 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(SMBcheckpath, 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 0000000..b18bc87
--- /dev/null
+++ b/source4/libcli/raw/rawfileinfo.c
@@ -0,0 +1,810 @@
+/*
+ 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) {
+ unsigned int n = io->num_streams;
+ uint32_t nlen, len;
+ bool ret;
+ void *vstr;
+ size_t converted_size = 0;
+
+ 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, &converted_size);
+ 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:
+ case RAW_FILEINFO_SMB2_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_NORMALIZED_NAME_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(4);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &parms->normalized_name_info.out.fname,
+ 0, 4, STR_UNICODE);
+ 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,
+ 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:
+ if (session == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ 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:
+ if (session == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ 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_NORMALIZED_NAME_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_NORMALIZED_NAME_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);
+
+ case RAW_FILEINFO_SMB2_ALT_NAME_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_SMB2_ALT_NAME_INFORMATION, 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 (req == NULL) {
+ goto failed;
+ }
+
+ 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 (req == NULL) {
+ goto failed;
+ }
+
+ 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 0000000..bf149ee
--- /dev/null
+++ b/source4/libcli/raw/rawfsinfo.c
@@ -0,0 +1,431 @@
+/*
+ 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 (req == NULL) {
+ return NULL;
+ }
+
+ 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;
+ 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: {
+ DATA_BLOB b2 = data_blob_const(blob.data, MIN(16, blob.length));
+ QFS_CHECK_SIZE(64);
+ status = GUID_from_ndr_blob(&b2, &fsinfo->objectid_information.out.guid);
+ NT_STATUS_NOT_OK_RETURN(status);
+ for (i=0;i<6;i++) {
+ fsinfo->objectid_information.out.unknown[i] = BVAL(blob.data, 16 + i*8);
+ }
+ break;
+
+ case RAW_QFS_SECTOR_SIZE_INFORMATION:
+ QFS_CHECK_SIZE(28);
+ fsinfo->sector_size_info.out.logical_bytes_per_sector
+ = IVAL(blob.data, 0);
+ fsinfo->sector_size_info.out.phys_bytes_per_sector_atomic
+ = IVAL(blob.data, 4);
+ fsinfo->sector_size_info.out.phys_bytes_per_sector_perf
+ = IVAL(blob.data, 8);
+ fsinfo->sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic
+ = IVAL(blob.data, 12);
+ fsinfo->sector_size_info.out.flags = IVAL(blob.data, 16);
+ fsinfo->sector_size_info.out.byte_off_sector_align
+ = IVAL(blob.data, 20);
+ fsinfo->sector_size_info.out.byte_off_partition_align
+ = IVAL(blob.data, 24);
+ 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);
+
+ case RAW_QFS_SECTOR_SIZE_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_SECTOR_SIZE_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);
+}
+
+/****************************************************************************
+ Set FSInfo raw interface (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_setfsinfo_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_setfsinfo *set_fsinfo)
+{
+ DATA_BLOB blob = data_blob_null;
+ NTSTATUS status;
+
+ if (set_fsinfo->generic.level != RAW_SETFS_UNIX_INFO) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = smb_raw_qfsinfo_blob_recv(req, mem_ctx, &blob);
+ data_blob_free(&blob);
+ return status;
+}
+
+/****************************************************************************
+ Set FSInfo raw interface (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_setfsinfo_send(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_setfsinfo *set_fsinfo)
+{
+ struct smb_trans2 tp;
+ uint16_t info_level;
+ uint16_t setup = TRANSACT2_SETFSINFO;
+
+ if (set_fsinfo->generic.level != RAW_SETFS_UNIX_INFO) {
+ return NULL;
+ }
+ 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.timeout = 0;
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 4);
+ if (!tp.in.params.data) {
+ return NULL;
+ }
+ info_level = (uint16_t)set_fsinfo->generic.level;
+ SSVAL(tp.in.params.data, 0, 0);
+ SSVAL(tp.in.params.data, 2, info_level);
+
+ tp.in.data = data_blob_talloc(mem_ctx, NULL, 12);
+ if (!tp.in.data.data) {
+ return NULL;
+ }
+
+ SSVAL(tp.in.data.data, 0, set_fsinfo->unix_info.in.major_version);
+ SSVAL(tp.in.data.data, 2, set_fsinfo->unix_info.in.minor_version);
+ SBVAL(tp.in.data.data, 4, set_fsinfo->unix_info.in.capability);
+
+ return smb_raw_trans2_send(tree, &tp);
+}
+
+/****************************************************************************
+ Set FSInfo raw interface (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_setfsinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_setfsinfo *set_fsinfo)
+{
+ struct smbcli_request *req = smb_raw_setfsinfo_send(tree, mem_ctx, set_fsinfo);
+ return smb_raw_setfsinfo_recv(req, mem_ctx, set_fsinfo);
+}
diff --git a/source4/libcli/raw/rawioctl.c b/source4/libcli/raw/rawioctl.c
new file mode 100644
index 0000000..77c7b03
--- /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 0000000..5c44772
--- /dev/null
+++ b/source4/libcli/raw/rawlpq.c
@@ -0,0 +1,48 @@
+/*
+ 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 "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 0000000..6d1b736
--- /dev/null
+++ b/source4/libcli/raw/rawnegotiate.c
@@ -0,0 +1,183 @@
+/*
+ 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 <tevent.h>
+#include "system/time.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "../lib/util/tevent_ntstatus.h"
+
+NTSTATUS smb_raw_negotiate_fill_transport(struct smbcli_transport *transport)
+{
+ struct smbcli_negotiate *n = &transport->negotiate;
+ struct smbXcli_conn *c = transport->conn;
+ NTTIME ntt;
+
+ n->protocol = smbXcli_conn_protocol(c);
+ if (n->protocol > PROTOCOL_NT1) {
+ return NT_STATUS_REVISION_MISMATCH;
+ }
+
+ n->sec_mode = smb1cli_conn_server_security_mode(c);
+ n->max_mux = smbXcli_conn_max_requests(c);
+ n->max_xmit = smb1cli_conn_max_xmit(c);
+ n->sesskey = smb1cli_conn_server_session_key(c);
+ n->capabilities = smb1cli_conn_capabilities(c);;
+
+ /* this time arrives in real GMT */
+ ntt = smbXcli_conn_server_system_time(c);
+ n->server_time = nt_time_to_unix(ntt);
+ n->server_zone = smb1cli_conn_server_time_zone(c);
+
+ if (n->capabilities & CAP_EXTENDED_SECURITY) {
+ const DATA_BLOB *b = smbXcli_conn_server_gss_blob(c);
+ if (b) {
+ n->secblob = *b;
+ }
+ } else {
+ const uint8_t *p = smb1cli_conn_server_challenge(c);
+ if (p) {
+ n->secblob = data_blob_const(p, 8);
+ }
+ }
+
+ n->readbraw_supported = smb1cli_conn_server_readbraw(c);
+ n->readbraw_supported = smb1cli_conn_server_writebraw(c);
+ n->lockread_supported = smb1cli_conn_server_lockread(c);
+
+ return NT_STATUS_OK;
+}
+
+struct smb_raw_negotiate_state {
+ struct smbcli_transport *transport;
+};
+
+static void smb_raw_negotiate_done(struct tevent_req *subreq);
+
+struct tevent_req *smb_raw_negotiate_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smbcli_transport *transport,
+ int minprotocol,
+ int maxprotocol)
+{
+ struct tevent_req *req;
+ struct smb_raw_negotiate_state *state;
+ struct tevent_req *subreq;
+ uint32_t timeout_msec = transport->options.request_timeout * 1000;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct smb_raw_negotiate_state);;
+ if (req == NULL) {
+ return NULL;
+ }
+ state->transport = transport;
+
+ if (maxprotocol > PROTOCOL_NT1) {
+ maxprotocol = PROTOCOL_NT1;
+ }
+
+ if (minprotocol > maxprotocol) {
+ minprotocol = maxprotocol;
+ }
+
+ subreq = smbXcli_negprot_send(state, ev,
+ transport->conn,
+ timeout_msec,
+ minprotocol,
+ maxprotocol,
+ transport->options.max_credits,
+ NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smb_raw_negotiate_done, req);
+
+ return req;
+}
+
+static void smb_raw_negotiate_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct smb_raw_negotiate_state *state =
+ tevent_req_data(req,
+ struct smb_raw_negotiate_state);
+ NTSTATUS status;
+
+ status = smbXcli_negprot_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ status = smb_raw_negotiate_fill_transport(state->transport);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+/*
+ Send a negprot command.
+*/
+NTSTATUS smb_raw_negotiate_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+
+/*
+ Send a negprot command (sync interface)
+*/
+NTSTATUS smb_raw_negotiate(struct smbcli_transport *transport, bool unicode,
+ int minprotocol, int maxprotocol)
+{
+ NTSTATUS status;
+ struct tevent_req *subreq = NULL;
+ bool ok;
+
+ subreq = smb_raw_negotiate_send(transport,
+ transport->ev,
+ transport,
+ minprotocol,
+ maxprotocol);
+ if (subreq == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ ok = tevent_req_poll(subreq, transport->ev);
+ if (!ok) {
+ status = map_nt_error_from_unix_common(errno);
+ goto failed;
+ }
+
+ status = smb_raw_negotiate_recv(subreq);
+
+failed:
+ TALLOC_FREE(subreq);
+ return status;
+}
diff --git a/source4/libcli/raw/rawnotify.c b/source4/libcli/raw/rawnotify.c
new file mode 100644
index 0000000..dcb979b
--- /dev/null
+++ b/source4/libcli/raw/rawnotify.c
@@ -0,0 +1,122 @@
+/*
+ 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 <tevent.h>
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.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);
+ if (next % 4 != 0)
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ 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;
+}
+
+/****************************************************************************
+ 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)
+{
+ bool ok;
+
+ if (oldreq->subreqs[0] == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ ok = tevent_req_cancel(oldreq->subreqs[0]);
+ if (!ok) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/raw/rawreadwrite.c b/source4/libcli/raw/rawreadwrite.c
new file mode 100644
index 0000000..fb44ba4
--- /dev/null
+++ b/source4/libcli/raw/rawreadwrite.c
@@ -0,0 +1,345 @@
+/*
+ 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;
+ }
+
+ 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));
+ parms->readx.out.flags2 = req->flags2;
+ parms->readx.out.data_offset = SVAL(req->in.vwv, VWV(6));
+
+ /* 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)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_write_recv(struct smbcli_request *req, union smb_write *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ !NT_STATUS_IS_OK(req->status)) {
+ 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 0000000..3a6cf5d
--- /dev/null
+++ b/source4/libcli/raw/rawrequest.c
@@ -0,0 +1,1055 @@
+/*
+ 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/events/events.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "../libcli/smb/smbXcli_base.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->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;
+}
+
+
+/*
+ setup a SMB packet at transport level
+*/
+struct smbcli_request *smbcli_request_setup_transport(struct smbcli_transport *transport,
+ uint8_t command, unsigned int wct, unsigned int buflen)
+{
+ struct smbcli_request *req;
+ size_t size;
+
+ size = NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen;
+
+ req = talloc_zero(transport, struct smbcli_request);
+ if (!req) {
+ return NULL;
+ }
+
+ /* setup the request context */
+ req->state = SMBCLI_REQUEST_INIT;
+ req->transport = transport;
+ req->out.size = size;
+
+ /* over allocate by a small amount */
+ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
+
+ req->out.buffer = talloc_zero_array(req, uint8_t, req->out.allocated);
+ if (!req->out.buffer) {
+ 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);
+
+ /* 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, 0);
+ 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, unsigned int wct, size_t buflen)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup_transport(session->transport, command, wct, buflen);
+
+ if (!req) return NULL;
+
+ smb1cli_session_set_id(session->smbXcli, session->vuid);
+
+ 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,
+ unsigned int wct, unsigned int buflen)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup_session(tree->session, command, wct, buflen);
+ if (req) {
+ smb1cli_tcon_set_id(tree->smbXcli, tree->tid);
+
+ 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, unsigned int 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, unsigned int 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,
+ unsigned int wct, size_t buflen)
+{
+ size_t wct_ofs;
+ size_t size;
+
+ /*
+ * here we only support one chained command
+ * If someone needs longer chains, the low
+ * level code should be used directly.
+ */
+ if (req->subreqs[0] != NULL) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+ if (req->subreqs[1] != NULL) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ req->subreqs[0] = smbcli_transport_setup_subreq(req);
+ if (req->subreqs[0] == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ wct_ofs = smb1cli_req_wct_ofs(req->subreqs, 1);
+
+ size = NBT_HDR_SIZE + wct_ofs + 1 + VWV(wct) + 2 + buflen;
+
+ req->out.size = size;
+
+ /* over allocate by a small amount */
+ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
+
+ req->out.buffer = talloc_zero_array(req, uint8_t, req->out.allocated);
+ if (!req->out.buffer) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.vwv = req->out.hdr + wct_ofs;
+ 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);
+
+ /* 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, 0);
+ 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);
+
+ if (req->session != NULL) {
+ SSVAL(req->out.hdr, HDR_FLG2, req->session->flags2);
+ SSVAL(req->out.hdr, HDR_PID, req->session->pid & 0xFFFF);
+ SSVAL(req->out.hdr, HDR_PIDHIGH, req->session->pid >> 16);
+ SSVAL(req->out.hdr, HDR_UID, req->session->vuid);
+ }
+
+ if (req->tree != NULL) {
+ SSVAL(req->out.hdr, HDR_TID, req->tree->tid);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ advance to the next chained reply in a request
+*/
+NTSTATUS smbcli_chained_advance(struct smbcli_request *req)
+{
+ struct smbcli_transport *transport = req->transport;
+ uint8_t *hdr = NULL;
+ uint8_t wct = 0;
+ uint16_t *vwv = NULL;
+ uint32_t num_bytes = 0;
+ uint8_t *bytes = NULL;
+ struct iovec *recv_iov = NULL;
+ uint8_t *inbuf = NULL;
+
+ if (req->subreqs[0] != NULL) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+ if (req->subreqs[1] == NULL) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ req->status = smb1cli_req_recv(req->subreqs[1], req,
+ &recv_iov,
+ &hdr,
+ &wct,
+ &vwv,
+ NULL, /* pvwv_offset */
+ &num_bytes,
+ &bytes,
+ NULL, /* pbytes_offset */
+ &inbuf,
+ NULL, 0); /* expected */
+ TALLOC_FREE(req->subreqs[1]);
+ if (!NT_STATUS_IS_OK(req->status)) {
+ if (recv_iov == NULL) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ return req->status;
+ }
+ }
+
+ /* fill in the 'in' portion of the matching request */
+ req->in.buffer = inbuf;
+ req->in.size = NBT_HDR_SIZE + PTR_DIFF(bytes, hdr) + num_bytes;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = hdr;
+ req->in.vwv = (uint8_t *)vwv;
+ req->in.wct = wct;
+ req->in.data = bytes;
+ req->in.data_size = num_bytes;
+ req->in.ptr = req->in.data;
+ req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
+
+ smb_setup_bufinfo(req);
+
+ transport->error.e.nt_status = req->status;
+ if (NT_STATUS_IS_OK(req->status)) {
+ transport->error.etype = ETYPE_NONE;
+ } else {
+ transport->error.etype = ETYPE_SMB;
+ }
+
+ req->state = SMBCLI_REQUEST_DONE;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ send a message
+*/
+bool smbcli_request_send(struct smbcli_request *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 (tevent_loop_once(req->transport->ev) != 0) {
+ return false;
+ }
+ }
+
+ return req->state == SMBCLI_REQUEST_DONE;
+}
+
+/*
+ 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, unsigned int 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, unsigned int 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, unsigned int 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)
+{
+ if (blob->length > 0) {
+ 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)
+{
+ if (byte_len > 0) {
+ 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, unsigned int 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);
+ 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, unsigned int 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);
+
+ 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, unsigned int 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, unsigned int 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);
+ 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, unsigned int 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);
+
+ 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,
+ unsigned int 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,
+ unsigned int 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, unsigned int 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
+ */
+NTSTATUS smbcli_pull_guid(void *base, uint16_t offset,
+ struct GUID *guid)
+{
+ DATA_BLOB blob;
+
+ ZERO_STRUCTP(guid);
+
+ blob.data = offset + (uint8_t *)base;
+ blob.length = 16;
+
+ return GUID_from_ndr_blob(&blob, guid);
+}
+
+/*
+ push a guid onto the wire. The buffer must hold 16 bytes
+ */
+NTSTATUS smbcli_push_guid(void *base, uint16_t offset, const struct GUID *guid)
+{
+ NTSTATUS status;
+ struct GUID_ndr_buf buf = { .buf = {0}, };
+
+ status = GUID_to_ndr_buf(guid, &buf);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ memcpy(offset + (uint8_t *)base, buf.buf, sizeof(buf.buf));
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/raw/rawsearch.c b/source4/libcli/raw/rawsearch.c
new file mode 100644
index 0000000..665dfd3
--- /dev/null
+++ b/source4/libcli/raw/rawsearch.c
@@ -0,0 +1,842 @@
+/*
+ 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,
+ unsigned int *next_ofs,
+ unsigned int str_flags)
+{
+ unsigned int 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);
+ memcpy(data->id_both_directory_info.short_name_buf, blob->data + 70, 24);
+ 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)
+{
+ unsigned int 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: {
+ unsigned int 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;
+ unsigned int 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 = data_blob_null, d_blob = data_blob_null;
+ 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 = data_blob_null, d_blob = data_blob_null;
+ 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 0000000..fec99d3
--- /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)
+{
+ unsigned int 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);
+ SIVAL(blob->data, 4, 0);
+ 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, 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:
+ case RAW_SFILEINFO_LINK_INFORMATION:
+ 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 0000000..7e48f26
--- /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/smb/smb_constants.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 0000000..1a1c836
--- /dev/null
+++ b/source4/libcli/raw/rawtrans.c
@@ -0,0 +1,446 @@
+/*
+ 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 <tevent.h>
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+static void smb_raw_trans_backend_done(struct tevent_req *subreq);
+
+static struct smbcli_request *smb_raw_trans_backend_send(struct smbcli_tree *tree,
+ struct smb_trans2 *parms,
+ uint8_t command)
+{
+ struct smbcli_request *req;
+ uint8_t additional_flags;
+ uint8_t clear_flags;
+ uint16_t additional_flags2;
+ uint16_t clear_flags2;
+ uint32_t pid;
+ struct smbXcli_tcon *tcon = NULL;
+ struct smbXcli_session *session = NULL;
+ const char *pipe_name = NULL;
+ uint8_t s;
+ uint32_t timeout_msec;
+ uint32_t tmp;
+
+ tmp = parms->in.params.length + parms->in.data.length;
+
+ req = smbcli_request_setup(tree, command, parms->in.setup_count, tmp);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ additional_flags = CVAL(req->out.hdr, HDR_FLG);
+ additional_flags2 = SVAL(req->out.hdr, HDR_FLG2);
+ pid = SVAL(req->out.hdr, HDR_PID);
+ pid |= SVAL(req->out.hdr, HDR_PIDHIGH)<<16;
+
+ if (req->session) {
+ session = req->session->smbXcli;
+ }
+
+ if (req->tree) {
+ tcon = req->tree->smbXcli;
+ }
+
+ clear_flags = ~additional_flags;
+ clear_flags2 = ~additional_flags2;
+
+ timeout_msec = req->transport->options.request_timeout * 1000;
+
+ for (s=0; s < parms->in.setup_count; s++) {
+ SSVAL(req->out.vwv, VWV(s), parms->in.setup[s]);
+ }
+
+ if (parms->in.params.length > 0) {
+ memcpy(req->out.data,
+ parms->in.params.data,
+ parms->in.params.length);
+ }
+ if (parms->in.data.length > 0) {
+ memcpy(req->out.data + parms->in.params.length,
+ parms->in.data.data,
+ parms->in.data.length);
+ }
+
+ if (command == SMBtrans && parms->in.trans_name) {
+ pipe_name = parms->in.trans_name;
+ }
+
+ req->subreqs[0] = smb1cli_trans_send(req,
+ req->transport->ev,
+ req->transport->conn,
+ command,
+ additional_flags,
+ clear_flags,
+ additional_flags2,
+ clear_flags2,
+ timeout_msec,
+ pid,
+ tcon,
+ session,
+ pipe_name,
+ 0xFFFF, /* fid */
+ 0, /* function */
+ parms->in.flags,
+ (uint16_t *)req->out.vwv,
+ parms->in.setup_count,
+ parms->in.max_setup,
+ req->out.data,
+ parms->in.params.length,
+ parms->in.max_param,
+ req->out.data+
+ parms->in.params.length,
+ parms->in.data.length,
+ parms->in.max_data);
+ if (req->subreqs[0] == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+ tevent_req_set_callback(req->subreqs[0],
+ smb_raw_trans_backend_done,
+ req);
+
+ return req;
+}
+
+static void smb_raw_trans_backend_done(struct tevent_req *subreq)
+{
+ struct smbcli_request *req =
+ tevent_req_callback_data(subreq,
+ struct smbcli_request);
+ struct smbcli_transport *transport = req->transport;
+ uint16_t *setup = NULL;
+ uint8_t num_setup = 0;
+ uint8_t s;
+ uint8_t *param = NULL;
+ uint32_t num_param = 0;
+ uint8_t *data = NULL;
+ uint32_t num_data = 0;
+
+ req->status = smb1cli_trans_recv(req->subreqs[0], req,
+ &req->flags2,
+ &setup,
+ 0, /* min_setup */
+ &num_setup,
+ &param,
+ 0, /* min_param */
+ &num_param,
+ &data,
+ 0, /* min_data */
+ &num_data);
+ TALLOC_FREE(req->subreqs[0]);
+ if (NT_STATUS_IS_ERR(req->status)) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ transport->error.e.nt_status = req->status;
+ transport->error.etype = ETYPE_SMB;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+
+ req->trans2.out.setup_count = num_setup;
+ req->trans2.out.setup = talloc_array(req, uint16_t, num_setup);
+ if (req->trans2.out.setup == NULL) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ req->status = NT_STATUS_NO_MEMORY;
+ transport->error.e.nt_status = req->status;
+ transport->error.etype = ETYPE_SMB;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+ for (s = 0; s < num_setup; s++) {
+ req->trans2.out.setup[s] = SVAL(setup, VWV(s));
+ }
+
+ req->trans2.out.params.data = param;
+ req->trans2.out.params.length = num_param;
+
+ req->trans2.out.data.data = data;
+ req->trans2.out.data.length = num_data;
+
+ transport->error.e.nt_status = req->status;
+ if (NT_STATUS_IS_OK(req->status)) {
+ transport->error.etype = ETYPE_NONE;
+ } else {
+ transport->error.etype = ETYPE_SMB;
+ }
+
+ req->state = SMBCLI_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+static NTSTATUS smb_raw_trans_backend_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ parms->out = req->trans2.out;
+ talloc_steal(mem_ctx, parms->out.setup);
+ talloc_steal(mem_ctx, parms->out.params.data);
+ talloc_steal(mem_ctx, parms->out.data.data);
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+_PUBLIC_ struct smbcli_request *smb_raw_trans_send(struct smbcli_tree *tree,
+ struct smb_trans2 *parms)
+{
+ return smb_raw_trans_backend_send(tree, parms, SMBtrans);
+}
+
+_PUBLIC_ NTSTATUS smb_raw_trans_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ return smb_raw_trans_backend_recv(req, mem_ctx, parms);
+}
+
+_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 smbcli_request *smb_raw_trans2_send(struct smbcli_tree *tree,
+ struct smb_trans2 *parms)
+{
+ return smb_raw_trans_backend_send(tree, parms, SMBtrans2);
+}
+
+NTSTATUS smb_raw_trans2_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ return smb_raw_trans_backend_recv(req, mem_ctx, parms);
+}
+
+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);
+}
+
+static void smb_raw_nttrans_done(struct tevent_req *subreq);
+
+struct smbcli_request *smb_raw_nttrans_send(struct smbcli_tree *tree,
+ struct smb_nttrans *parms)
+{
+ struct smbcli_request *req;
+ uint8_t additional_flags;
+ uint8_t clear_flags;
+ uint16_t additional_flags2;
+ uint16_t clear_flags2;
+ uint32_t pid;
+ struct smbXcli_tcon *tcon = NULL;
+ struct smbXcli_session *session = NULL;
+ uint32_t timeout_msec;
+ uint32_t tmp;
+
+ tmp = parms->in.params.length + parms->in.data.length;
+
+ req = smbcli_request_setup(tree, SMBnttrans, parms->in.setup_count, tmp);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ additional_flags = CVAL(req->out.hdr, HDR_FLG);
+ additional_flags2 = SVAL(req->out.hdr, HDR_FLG2);
+ pid = SVAL(req->out.hdr, HDR_PID);
+ pid |= SVAL(req->out.hdr, HDR_PIDHIGH)<<16;
+
+ if (req->session) {
+ session = req->session->smbXcli;
+ }
+
+ if (req->tree) {
+ tcon = req->tree->smbXcli;
+ }
+
+ clear_flags = ~additional_flags;
+ clear_flags2 = ~additional_flags2;
+
+ timeout_msec = req->transport->options.request_timeout * 1000;
+
+ if (parms->in.setup_count > 0) {
+ memcpy(
+ req->out.vwv, parms->in.setup, parms->in.setup_count * 2);
+ }
+
+ if (parms->in.params.length > 0) {
+ memcpy(req->out.data,
+ parms->in.params.data,
+ parms->in.params.length);
+ }
+ if (parms->in.data.length > 0) {
+ memcpy(req->out.data + parms->in.params.length,
+ parms->in.data.data,
+ parms->in.data.length);
+ }
+
+ req->subreqs[0] = smb1cli_trans_send(req,
+ req->transport->ev,
+ req->transport->conn,
+ SMBnttrans,
+ additional_flags,
+ clear_flags,
+ additional_flags2,
+ clear_flags2,
+ timeout_msec,
+ pid,
+ tcon,
+ session,
+ NULL, /* pipe_name */
+ 0xFFFF, /* fid */
+ parms->in.function,
+ 0, /* flags */
+ (uint16_t *)req->out.vwv,
+ parms->in.setup_count,
+ parms->in.max_setup,
+ req->out.data,
+ parms->in.params.length,
+ parms->in.max_param,
+ req->out.data+
+ parms->in.params.length,
+ parms->in.data.length,
+ parms->in.max_data);
+ if (req->subreqs[0] == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+ tevent_req_set_callback(req->subreqs[0],
+ smb_raw_nttrans_done,
+ req);
+
+ return req;
+}
+
+static void smb_raw_nttrans_done(struct tevent_req *subreq)
+{
+ struct smbcli_request *req =
+ tevent_req_callback_data(subreq,
+ struct smbcli_request);
+ struct smbcli_transport *transport = req->transport;
+ uint16_t *setup = NULL;
+ uint8_t num_setup = 0;
+ uint8_t *param = NULL;
+ uint32_t num_param = 0;
+ uint8_t *data = NULL;
+ uint32_t num_data = 0;
+
+ req->status = smb1cli_trans_recv(req->subreqs[0], req,
+ &req->flags2,
+ &setup,
+ 0, /* min_setup */
+ &num_setup,
+ &param,
+ 0, /* min_param */
+ &num_param,
+ &data,
+ 0, /* min_data */
+ &num_data);
+ TALLOC_FREE(req->subreqs[0]);
+ if (NT_STATUS_IS_ERR(req->status)) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ transport->error.e.nt_status = req->status;
+ transport->error.etype = ETYPE_SMB;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+
+ req->nttrans.out.setup_count = num_setup;
+ req->nttrans.out.setup = (uint8_t *)setup;
+
+ req->nttrans.out.params.data = param;
+ req->nttrans.out.params.length = num_param;
+
+ req->nttrans.out.data.data = data;
+ req->nttrans.out.data.length = num_data;
+
+ transport->error.e.nt_status = req->status;
+ if (NT_STATUS_IS_OK(req->status)) {
+ transport->error.etype = ETYPE_NONE;
+ } else {
+ transport->error.etype = ETYPE_SMB;
+ }
+
+ req->state = SMBCLI_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+NTSTATUS smb_raw_nttrans_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_nttrans *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ parms->out = req->nttrans.out;
+ talloc_steal(mem_ctx, parms->out.setup);
+ talloc_steal(mem_ctx, parms->out.params.data);
+ talloc_steal(mem_ctx, parms->out.data.data);
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+/****************************************************************************
+ 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 0000000..dd38b31
--- /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;
+ unsigned int 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 0000000..933db4d
--- /dev/null
+++ b/source4/libcli/raw/signing.h
@@ -0,0 +1,39 @@
+#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
+};
+
+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 0000000..780260e
--- /dev/null
+++ b/source4/libcli/raw/smb.h
@@ -0,0 +1,322 @@
+/*
+ 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 _RAW_SMB_H
+#define _RAW_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 | \
+ 0xFF000000)
+
+/*
+ * private_flags field in ntcreatex
+ * This values have different meaning for some ntvfs backends.
+ */
+#define NTCREATEX_FLAG_DENY_DOS 0x0001
+#define NTCREATEX_FLAG_DENY_FCB 0x0002
+
+
+/* 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
+
+/* 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
+
+/* 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
+
+/* 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)
+
+#define FILE_NOTIFY_CHANGE_ALL \
+ (FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | \
+ FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | \
+ FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS | \
+ FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA | \
+ FILE_NOTIFY_CHANGE_SECURITY | FILE_NOTIFY_CHANGE_STREAM_NAME | \
+ FILE_NOTIFY_CHANGE_STREAM_SIZE | FILE_NOTIFY_CHANGE_STREAM_WRITE)
+
+/* 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) (((const 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
+
+/*
+ * Global value meaning that the smb_uid field should be
+ * ingored (in share level security and protocol level == CORE)
+ */
+
+#define UID_FIELD_INVALID 0
+
+/*
+ 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
+
+#include "source4/libcli/raw/trans2.h"
+#include "libcli/raw/interfaces.h"
+#include "libcli/smb/smb_common.h"
+
+#endif /* _RAW_SMB_H */
diff --git a/source4/libcli/raw/smb_signing.c b/source4/libcli/raw/smb_signing.c
new file mode 100644
index 0000000..6747fc3
--- /dev/null
+++ b/source4/libcli/raw/smb_signing.c
@@ -0,0 +1,276 @@
+/*
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "../lib/crypto/crypto.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/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;
+}
+
+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];
+ gnutls_hash_hd_t hash_hnd = NULL;
+ int rc;
+
+ /*
+ * 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. */
+ rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
+ if (rc < 0) {
+ return;
+ }
+
+ rc = gnutls_hash(hash_hnd, mac_key->data, mac_key->length);
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ return;
+ }
+ rc = gnutls_hash(hash_hnd,
+ out->buffer + NBT_HDR_SIZE,
+ out->size - NBT_HDR_SIZE);
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ return;
+ }
+ gnutls_hash_deinit(hash_hnd, calc_md5_mac);
+
+ 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);
+ ZERO_ARRAY(calc_md5_mac);
+/* 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, unsigned int seq_num)
+{
+ bool ok = false;
+ uint8_t calc_md5_mac[16];
+ uint8_t *server_sent_mac;
+ uint8_t sequence_buf[8];
+ gnutls_hash_hd_t hash_hnd;
+ const size_t offset_end_of_sig = (HDR_SS_FIELD + 8);
+ int rc;
+ 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. */
+ rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
+ if (rc < 0) {
+ ok = false;
+ goto out;
+ }
+
+ rc = gnutls_hash(hash_hnd, mac_key->data, mac_key->length);
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ ok = false;
+ goto out;
+ }
+ rc = gnutls_hash(hash_hnd, in->hdr, HDR_SS_FIELD);
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ ok = false;
+ goto out;
+ }
+ rc = gnutls_hash(hash_hnd, sequence_buf, sizeof(sequence_buf));
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ ok = false;
+ goto out;
+ }
+ rc = gnutls_hash(hash_hnd,
+ in->hdr + offset_end_of_sig,
+ in->size - NBT_HDR_SIZE - (offset_end_of_sig));
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ ok = false;
+ goto out;
+ }
+
+ gnutls_hash_deinit(hash_hnd, calc_md5_mac);
+
+ ok = mem_equal_const_time(server_sent_mac, calc_md5_mac, 8);
+
+ if (i == 0) {
+ if (!ok) {
+ 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);
+ }
+ }
+ ZERO_ARRAY(calc_md5_mac);
+
+ if (ok) break;
+ }
+
+ if (ok && i != 0) {
+ DEBUG(0,("SIGNING OFFSET %d (should be %d)\n", i, seq_num));
+ }
+
+out:
+ return ok;
+}
+
+/**
+ 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 - 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;
+}
+
diff --git a/source4/libcli/raw/trans2.h b/source4/libcli/raw/trans2.h
new file mode 100644
index 0000000..f93b1f1
--- /dev/null
+++ b/source4/libcli/raw/trans2.h
@@ -0,0 +1,309 @@
+/*
+ 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_
+
+
+/* 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
+#define SMB_QFS_SECTOR_SIZE_INFORMATION 1011
+
+
+/* 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
+#define SMB_QFILEINFO_NORMALIZED_NAME_INFORMATION 1048
+
+
+
+/* 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
+
+/* SMB_QFS_SECTOR_SIZE_INFORMATION values */
+#define QFS_SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001
+#define QFS_SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002
+#define QFS_SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004
+#define QFS_SSINFO_FLAGS_TRIM_ENABLED 0x00000008
+
+#define QFS_SSINFO_OFFSET_UNKNOWN 0xffffffff
+
+/*
+ * 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
+
+#endif
diff --git a/source4/libcli/resolve/bcast.c b/source4/libcli/resolve/bcast.c
new file mode 100644
index 0000000..277a82d
--- /dev/null
+++ b/source4/libcli/resolve/bcast.c
@@ -0,0 +1,117 @@
+/*
+ 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
+ */
+static 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_list_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++) {
+ bool ipv4 = iface_list_n_is_v4(data->ifaces, i);
+ const char *bcast;
+
+ if (!ipv4) {
+ continue;
+ }
+
+ bcast = iface_list_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
+ */
+static 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_interface_list(ctx, lp_ctx, &ifaces);
+ return resolve_context_add_bcast_method(ctx, ifaces, lpcfg_nbt_port(lp_ctx), lpcfg_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 0000000..b8447be
--- /dev/null
+++ b/source4/libcli/resolve/dns_ex.c
@@ -0,0 +1,671 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ async getaddrinfo()/dns_lookup() name resolution module
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher 2008
+ Copyright (C) Matthieu Patou 2011
+
+ 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 "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"
+#include "lib/util/util_net.h"
+#include "lib/addns/dnsquery.h"
+#include "lib/addns/dns.h"
+#include "lib/util/sys_rw.h"
+#include "lib/util/smb_strtox.h"
+#include <arpa/nameser.h>
+#include <resolv.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);
+ if (waitpid(state->child, &status, WNOHANG) == 0) {
+ kill(state->child, SIGKILL);
+ waitpid(state->child, &status, 0);
+ }
+
+ return 0;
+}
+
+struct dns_records_container {
+ char **list;
+ uint32_t count;
+};
+
+static int reply_to_addrs(TALLOC_CTX *mem_ctx, uint32_t *a_num,
+ char ***cur_addrs, uint32_t total,
+ struct dns_request *reply, int port)
+{
+ char addrstr[INET6_ADDRSTRLEN];
+ struct dns_rrec *rr;
+ char **addrs;
+ uint32_t i;
+ const char *addr;
+
+ /* at most we over-allocate here, but not by much */
+ addrs = talloc_realloc(mem_ctx, *cur_addrs, char *,
+ total + reply->num_answers);
+ if (!addrs) {
+ return 0;
+ }
+ *cur_addrs = addrs;
+
+ for (i = 0; i < reply->num_answers; i++) {
+ rr = reply->answers[i];
+
+ /* we are only interested in the IN class */
+ if (rr->r_class != DNS_CLASS_IN) {
+ continue;
+ }
+
+ if (rr->type == QTYPE_NS) {
+ /*
+ * After the record for NS will come the A or AAAA
+ * record of the NS.
+ */
+ break;
+ }
+
+ /* verify we actually have a record here */
+ if (!rr->data) {
+ continue;
+ }
+
+ /* we are only interested in A and AAAA records */
+ switch (rr->type) {
+ case QTYPE_A:
+ addr = inet_ntop(AF_INET,
+ (struct in_addr *)rr->data,
+ addrstr, sizeof(addrstr));
+ if (addr == NULL) {
+ continue;
+ }
+ break;
+ case QTYPE_AAAA:
+#ifdef HAVE_IPV6
+ addr = inet_ntop(AF_INET6,
+ (struct in6_addr *)rr->data,
+ addrstr, sizeof(addrstr));
+#else
+ addr = NULL;
+#endif
+ if (addr == NULL) {
+ continue;
+ }
+ break;
+ default:
+ continue;
+ }
+
+ addrs[total] = talloc_asprintf(addrs, "%s@%u/%s",
+ addrstr, port,
+ rr->name->pLabelList->label);
+ if (addrs[total]) {
+ total++;
+ if (rr->type == QTYPE_A) {
+ (*a_num)++;
+ }
+ }
+ }
+
+ return total;
+}
+
+static DNS_ERROR dns_lookup(TALLOC_CTX *mem_ctx, const char* name,
+ uint16_t q_type, struct dns_request **reply)
+{
+ int len, rlen;
+ uint8_t *answer;
+ bool loop;
+ struct dns_buffer buf;
+ DNS_ERROR err;
+
+ /* give space for a good sized answer by default */
+ answer = NULL;
+ len = 1500;
+ do {
+ answer = talloc_realloc(mem_ctx, answer, uint8_t, len);
+ if (!answer) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+ rlen = res_search(name, DNS_CLASS_IN, q_type, answer, len);
+ if (rlen == -1) {
+ if (len >= 65535) {
+ return ERROR_DNS_SOCKET_ERROR;
+ }
+ /* retry once with max packet size */
+ len = 65535;
+ loop = true;
+ } else if (rlen > len) {
+ len = rlen;
+ loop = true;
+ } else {
+ loop = false;
+ }
+ } while(loop);
+
+ buf.data = answer;
+ buf.size = rlen;
+ buf.offset = 0;
+ buf.error = ERROR_DNS_SUCCESS;
+
+ err = dns_unmarshall_request(mem_ctx, &buf, reply);
+
+ TALLOC_FREE(answer);
+ return err;
+}
+
+static struct dns_records_container get_a_aaaa_records(TALLOC_CTX *mem_ctx,
+ const char* name,
+ int port)
+{
+ struct dns_request *reply;
+ struct dns_records_container ret;
+ char **addrs = NULL;
+ uint32_t a_num, total;
+ uint16_t qtype;
+ TALLOC_CTX *tmp_ctx;
+ DNS_ERROR err;
+
+ memset(&ret, 0, sizeof(struct dns_records_container));
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return ret;
+ }
+
+ qtype = QTYPE_AAAA;
+
+ /* this is the blocking call we are going to lots of trouble
+ to avoid them in the parent */
+ err = dns_lookup(tmp_ctx, name, qtype, &reply);
+ if (!ERR_DNS_IS_OK(err)) {
+ qtype = QTYPE_A;
+ err = dns_lookup(tmp_ctx, name, qtype, &reply);
+ if (!ERR_DNS_IS_OK(err)) {
+ goto done;
+ }
+ }
+
+ a_num = total = 0;
+ total = reply_to_addrs(tmp_ctx, &a_num, &addrs, total, reply, port);
+
+ if (qtype == QTYPE_AAAA && a_num == 0) {
+ /*
+ * DNS server didn't returned A when asked for AAAA records.
+ * Most of the server do it, let's ask for A specificaly.
+ */
+ err = dns_lookup(tmp_ctx, name, QTYPE_A, &reply);
+ if (ERR_DNS_IS_OK(err)) {
+ /*
+ * Ignore an error here and just return any AAAA
+ * records we already got. This may be an IPv6-only
+ * config.
+ */
+ total = reply_to_addrs(tmp_ctx, &a_num, &addrs, total,
+ reply, port);
+ }
+ }
+
+ if (total) {
+ talloc_steal(mem_ctx, addrs);
+ ret.count = total;
+ ret.list = addrs;
+ }
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static struct dns_records_container get_srv_records(TALLOC_CTX *mem_ctx,
+ const char* name)
+{
+ struct dns_records_container ret = {0};
+ char **addrs = NULL;
+ struct dns_rr_srv *dclist;
+ NTSTATUS status;
+ size_t total;
+ size_t i;
+ size_t count = 0;
+
+ memset(&ret, 0, sizeof(struct dns_records_container));
+ /* this is the blocking call we are going to lots of trouble
+ to avoid them in the parent */
+ status = ads_dns_lookup_srv(mem_ctx, name, &dclist, &count);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ret;
+ }
+ total = 0;
+ if (count == 0) {
+ return ret;
+ }
+
+ /* Loop over all returned records and pick the records */
+ for (i = 0; i < count; i++) {
+ struct dns_records_container c;
+ const char* tmp_str;
+
+ tmp_str = dclist[i].hostname;
+ if (strchr(tmp_str, '.') && tmp_str[strlen(tmp_str)-1] != '.') {
+ /* we are asking for a fully qualified name, but the
+ name doesn't end in a '.'. We need to prevent the
+ DNS library trying the search domains configured in
+ resolv.conf */
+ tmp_str = talloc_asprintf(mem_ctx, "%s.", tmp_str);
+ }
+
+ c = get_a_aaaa_records(mem_ctx, tmp_str, dclist[i].port);
+
+ /* wrap check */
+ if (total + c.count < total) {
+ /* possibly could just break here instead? */
+ TALLOC_FREE(addrs);
+ return ret;
+ }
+ total += c.count;
+ if (addrs == NULL) {
+ addrs = c.list;
+ } else {
+ unsigned j;
+
+ addrs = talloc_realloc(mem_ctx, addrs, char*, total);
+ for (j=0; j < c.count; j++) {
+ addrs[total - j - 1] = talloc_steal(addrs, c.list[j]);
+ }
+ }
+ }
+
+ if (total) {
+ ret.count = total;
+ ret.list = addrs;
+ }
+
+ return ret;
+}
+/*
+ the blocking child
+*/
+static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
+{
+ bool first;
+ bool do_srv = (state->flags & RESOLVE_NAME_FLAG_DNS_SRV);
+ struct dns_records_container c;
+ char* addrs = NULL;
+ unsigned int i;
+
+ if (strchr(state->name.name, '.') && state->name.name[strlen(state->name.name)-1] != '.') {
+ /* we are asking for a fully qualified name, but the
+ name doesn't end in a '.'. We need to prevent the
+ DNS library trying the search domains configured in
+ resolv.conf */
+ state->name.name = talloc_strdup_append(discard_const_p(char, state->name.name),
+ ".");
+ }
+
+
+ if (do_srv) {
+ c = get_srv_records(state, state->name.name);
+ } else {
+ c = get_a_aaaa_records(state, state->name.name, state->port);
+ }
+
+ /* This line in critical - if we return without writing to the
+ * pipe, this is the signal that the name did not exist */
+ if (c.count == 0) {
+ goto done;
+ }
+
+ addrs = talloc_strdup(state, "");
+ if (!addrs) {
+ goto done;
+ }
+ first = true;
+
+ for (i=0; i < c.count; i++) {
+ addrs = talloc_asprintf_append_buffer(addrs, "%s%s",
+ first?"":",",
+ c.list[i]);
+ first = false;
+ }
+
+ if (addrs) {
+ DEBUG(11, ("Addrs = %s\n", addrs));
+ sys_write_v(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_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
+
+ ret = getaddrinfo(state->name.name, "0", &hints, &res_list);
+ /* try to fallback in case of error */
+ if (state->do_fallback) {
+ switch (ret) {
+#ifdef EAI_NODATA
+ case EAI_NODATA:
+#endif
+ case EAI_FAIL:
+ /* Linux returns EAI_NODATA on non-RFC1034-compliant names. FreeBSD returns EAI_FAIL */
+ case EAI_NONAME:
+ /* getaddrinfo() doesn't handle CNAME or non-RFC1034 compatible records */
+ run_child_dns_lookup(state, fd);
+ return;
+ default:
+ break;
+ }
+ }
+ if (ret != 0) {
+ goto done;
+ }
+
+ addrs = talloc_strdup(state, "");
+ if (!addrs) {
+ goto done;
+ }
+ first = true;
+ for (res = res_list; res; res = res->ai_next) {
+ char addrstr[INET6_ADDRSTRLEN];
+ if (!print_sockaddr_len(addrstr, sizeof(addrstr), (struct sockaddr *)res->ai_addr, res->ai_addrlen)) {
+ continue;
+ }
+ addrs = talloc_asprintf_append_buffer(addrs, "%s%s@%u/%s",
+ first?"":",",
+ addrstr,
+ state->port,
+ state->name.name);
+ if (!addrs) {
+ goto done;
+ }
+ first = false;
+ }
+
+ if (addrs) {
+ sys_write_v(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;
+ }
+ if (waitpid(state->child, &status, WNOHANG) == 0) {
+ kill(state->child, SIGKILL);
+ waitpid(state->child, &status, 0);
+ }
+
+ if (ret <= 0) {
+ /* The check for ret == 0 here is important, if the
+ * name does not exist, then no bytes are written to
+ * the pipe */
+ DEBUG(3,("dns child failed to find name '%s' of type %s\n",
+ state->name.name, (state->flags & RESOLVE_NAME_FLAG_DNS_SRV)?"SRV":"A"));
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ /* ensure 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;
+ int error = 0;
+
+ 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) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+ port = smb_strtoul(p, NULL, 10, &error, SMB_STR_STANDARD);
+ if (port > UINT16_MAX || error != 0) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+ state->addrs[i] = socket_address_from_strings(state->addrs,
+ "ip",
+ 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_common(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 = tevent_add_fd(c->event_ctx, c, state->child_fd, TEVENT_FD_READ,
+ pipe_handler, c);
+ if (composite_nomem(state->fde, c)) {
+ close(fd[0]);
+ close(fd[1]);
+ return c;
+ }
+ tevent_fd_set_auto_close(state->fde);
+
+ state->child = fork();
+ if (state->child == (pid_t)-1) {
+ composite_error(c, map_nt_error_from_unix_common(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 0000000..755a4e8
--- /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/lmhosts.c b/source4/libcli/resolve/lmhosts.c
new file mode 100644
index 0000000..244a9a3
--- /dev/null
+++ b/source4/libcli/resolve/lmhosts.c
@@ -0,0 +1,133 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ lmhosts name resolution module
+
+ Copyright (C) Andrew Tridgell 1994-1998,2005
+ Copyright (C) Jeremy Allison 2007
+ Copyright (C) Jelmer Vernooij 2007
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009-2014
+ 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 "lib/socket/socket.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+#include "lib/util/util_net.h"
+#include "libcli/nbt/libnbt.h"
+#include "dynconfig.h"
+
+struct resolve_lmhosts_state {
+ struct socket_address **addrs;
+ char **names;
+};
+
+/**
+ lmhosts name resolution method - async send
+ */
+/*
+ general name resolution - async send
+ */
+static struct composite_context *resolve_name_lmhosts_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ void *userdata, uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name)
+{
+ struct composite_context *c;
+ struct resolve_lmhosts_state *state;
+ struct sockaddr_storage *resolved_iplist;
+ size_t resolved_count = 0, i;
+
+ if (event_ctx == NULL) {
+ return NULL;
+ }
+
+ c = composite_create(mem_ctx, event_ctx);
+ if (c == NULL) return NULL;
+
+ if (composite_nomem(c->event_ctx, c)) return c;
+
+ state = talloc_zero(c, struct resolve_lmhosts_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ c->status = resolve_lmhosts_file_as_sockaddr(state,
+ dyn_LMHOSTSFILE,
+ name->name,
+ name->type,
+ &resolved_iplist,
+ &resolved_count);
+ if (!composite_is_ok(c)) return c;
+
+ for (i=0; i < resolved_count; i += 2) {
+ state->addrs = talloc_realloc(state, state->addrs, struct socket_address *, i+2);
+ if (composite_nomem(state->addrs, c)) return c;
+
+ set_sockaddr_port((struct sockaddr *)&resolved_iplist[i], port);
+
+ state->addrs[i] = socket_address_from_sockaddr(state->addrs, (struct sockaddr *)&resolved_iplist[i], sizeof(resolved_iplist[i]));
+ if (composite_nomem(state->addrs[i], c)) return c;
+
+ state->addrs[i+1] = NULL;
+
+
+ state->names = talloc_realloc(state, state->names, char *, i+2);
+ if (composite_nomem(state->addrs, c)) return c;
+
+ state->names[i] = talloc_strdup(state->names, name->name);
+ if (composite_nomem(state->names[i], c)) return c;
+
+ state->names[i+1] = NULL;
+
+ }
+
+ composite_done(c);
+ return c;
+}
+
+/*
+ general name resolution method - recv side
+ */
+static NTSTATUS resolve_name_lmhosts_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_lmhosts_state *state = talloc_get_type(c->private_data, struct resolve_lmhosts_state);
+ *addrs = talloc_steal(mem_ctx, state->addrs);
+ if (names) {
+ *names = talloc_steal(mem_ctx, state->names);
+ }
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+bool resolve_context_add_lmhosts_method(struct resolve_context *ctx)
+{
+ return resolve_context_add_method(ctx, resolve_name_lmhosts_send, resolve_name_lmhosts_recv, NULL);
+}
diff --git a/source4/libcli/resolve/nbtlist.c b/source4/libcli/resolve/nbtlist.c
new file mode 100644
index 0000000..13f38ff
--- /dev/null
+++ b/source4/libcli/resolve/nbtlist.c
@@ -0,0 +1,223 @@
+/*
+ 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 * const *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);
+ 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 0000000..db2606b
--- /dev/null
+++ b/source4/libcli/resolve/resolve.c
@@ -0,0 +1,342 @@
+/*
+ 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"
+#include "lib/tsocket/tsocket.h"
+#include "lib/util/util_net.h"
+
+#undef strcasecmp
+
+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);
+ 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,
+ TALLOC_CTX *mem_ctx,
+ uint32_t flags, /* RESOLVE_NAME_FLAG_* */
+ uint16_t port,
+ struct nbt_name *name,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *c;
+ struct resolve_state *state;
+
+ if (event_ctx == NULL) {
+ return NULL;
+ }
+
+ c = composite_create(mem_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) {
+ 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, "ip",
+ state->name.name, 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_ex_send(struct resolve_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t flags, /* RESOLVE_NAME_FLAG_* */
+ uint16_t port,
+ struct nbt_name *name,
+ struct tevent_context *event_ctx)
+{
+ return resolve_name_all_send(ctx, mem_ctx, flags, port, name, event_ctx);
+}
+
+struct composite_context *resolve_name_send(struct resolve_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_name *name,
+ struct tevent_context *event_ctx)
+{
+ return resolve_name_ex_send(ctx, mem_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)) {
+ struct tsocket_address *t_addr = socket_address_to_tsocket_address(addrs, addrs[0]);
+ if (!t_addr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *reply_addr = tsocket_address_inet_addr_string(t_addr, mem_ctx);
+ talloc_free(addrs);
+ if (!*reply_addr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ return status;
+}
+
+/*
+ receive multiple responses from resolve_name_send()
+ */
+NTSTATUS resolve_name_multiple_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ const char ***reply_addrs)
+{
+ NTSTATUS status;
+ struct socket_address **addrs = NULL;
+ int i;
+
+ status = resolve_name_all_recv(c, mem_ctx, &addrs, NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* count the addresses */
+ for (i=0; addrs[i]; i++) ;
+
+ *reply_addrs = talloc_array(mem_ctx, const char *, i+1);
+ NT_STATUS_HAVE_NO_MEMORY(*reply_addrs);
+
+ for (i=0; addrs[i]; i++) {
+ struct tsocket_address *t_addr = socket_address_to_tsocket_address(addrs, addrs[i]);
+ NT_STATUS_HAVE_NO_MEMORY(t_addr);
+
+ (*reply_addrs)[i] = tsocket_address_inet_addr_string(t_addr, *reply_addrs);
+ NT_STATUS_HAVE_NO_MEMORY((*reply_addrs)[i]);
+ }
+ (*reply_addrs)[i] = NULL;
+
+ talloc_free(addrs);
+
+ return status;
+}
+
+/*
+ general name resolution - sync call
+ */
+NTSTATUS resolve_name_ex(struct resolve_context *ctx,
+ uint32_t flags, /* RESOLVE_NAME_FLAG_* */
+ uint16_t port,
+ struct nbt_name *name,
+ TALLOC_CTX *mem_ctx,
+ const char **reply_addr,
+ struct tevent_context *ev)
+{
+ struct composite_context *c = resolve_name_ex_send(ctx, mem_ctx, flags, port, 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 0000000..8ace574
--- /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 0000000..56c7b01
--- /dev/null
+++ b/source4/libcli/resolve/resolve_lp.c
@@ -0,0 +1,52 @@
+/*
+ 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 *lpcfg_resolve_context(struct loadparm_context *lp_ctx)
+{
+ const char **methods = lpcfg_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")) {
+ if (lpcfg_disable_netbios(lp_ctx) == false) {
+ resolve_context_add_wins_method_lp(ret, lp_ctx);
+ }
+ } else if (!strcmp(methods[i], "bcast")) {
+ if (lpcfg_disable_netbios(lp_ctx) == false) {
+ resolve_context_add_bcast_method_lp(ret, lp_ctx);
+ }
+ } else if (!strcmp(methods[i], "lmhosts")) {
+ resolve_context_add_lmhosts_method(ret);
+ } 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 0000000..2bf6e46
--- /dev/null
+++ b/source4/libcli/resolve/testsuite.c
@@ -0,0 +1,92 @@
+/*
+ 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 "torture/local/proto.h"
+#include "system/network.h"
+#include "lib/util/util_net.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 0000000..541d3fe
--- /dev/null
+++ b/source4/libcli/resolve/wins.c
@@ -0,0 +1,83 @@
+/*
+ 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 {
+ 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,
+ (const char * const *)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 = 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_interface_list(ctx, lp_ctx, &ifaces);
+ return resolve_context_add_wins_method(ctx, lpcfg_wins_server_list(lp_ctx), ifaces, lpcfg_nbt_port(lp_ctx), lpcfg_parm_int(lp_ctx, NULL, "nbt", "timeout", 1));
+}
diff --git a/source4/libcli/smb2/break.c b/source4/libcli/smb2/break.c
new file mode 100644
index 0000000..fe0cceb
--- /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 0000000..cc40b34
--- /dev/null
+++ b/source4/libcli/smb2/cancel.c
@@ -0,0 +1,45 @@
+/*
+ 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 <tevent.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)
+{
+ bool ok;
+
+ if (r->subreq == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ ok = tevent_req_cancel(r->subreq);
+ if (!ok) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb2/close.c b/source4/libcli/smb2/close.c
new file mode 100644
index 0000000..4e6f330
--- /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/connect.c b/source4/libcli/smb2/connect.c
new file mode 100644
index 0000000..64b6786
--- /dev/null
+++ b/source4/libcli/smb2/connect.c
@@ -0,0 +1,483 @@
+/*
+ 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 <tevent.h>
+#include "lib/util/tevent_ntstatus.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"
+#include "auth/credentials/credentials.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "smb2_constants.h"
+
+struct smb2_connect_state {
+ struct tevent_context *ev;
+ struct cli_credentials *credentials;
+ bool fallback_to_anonymous;
+ uint64_t previous_session_id;
+ struct resolve_context *resolve_ctx;
+ const char *host;
+ const char *share;
+ const char *unc;
+ const char **ports;
+ const char *socket_options;
+ struct nbt_name calling, called;
+ struct gensec_settings *gensec_settings;
+ struct smbcli_options options;
+ struct smb2_transport *transport;
+ struct smb2_session *session;
+ struct smb2_tree *tree;
+};
+
+static void smb2_connect_session_start(struct tevent_req *req);
+static void smb2_connect_socket_done(struct composite_context *creq);
+
+/*
+ a composite function that does a full negprot/sesssetup/tcon, returning
+ a connected smb2_tree
+ */
+struct tevent_req *smb2_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *host,
+ const char **ports,
+ const char *share,
+ struct resolve_context *resolve_ctx,
+ struct cli_credentials *credentials,
+ bool fallback_to_anonymous,
+ struct smbXcli_conn **existing_conn,
+ uint64_t previous_session_id,
+ const struct smbcli_options *options,
+ const char *socket_options,
+ struct gensec_settings *gensec_settings)
+{
+ struct tevent_req *req;
+ struct smb2_connect_state *state;
+ struct composite_context *creq;
+ static const char *default_ports[] = { "445", "139", NULL };
+ enum smb_encryption_setting encryption_state =
+ cli_credentials_get_smb_encryption(credentials);
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct smb2_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->credentials = credentials;
+ state->fallback_to_anonymous = fallback_to_anonymous;
+ state->previous_session_id = previous_session_id;
+ state->options = *options;
+ state->host = host;
+ state->ports = ports;
+ state->share = share;
+ state->resolve_ctx = resolve_ctx;
+ state->socket_options = socket_options;
+ state->gensec_settings = gensec_settings;
+
+ if (state->ports == NULL) {
+ state->ports = default_ports;
+ }
+
+ if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
+ state->options.signing = SMB_SIGNING_REQUIRED;
+ }
+
+ make_nbt_name_client(&state->calling,
+ cli_credentials_get_workstation(credentials));
+
+ nbt_choose_called_name(state, &state->called,
+ host, NBT_NAME_SERVER);
+
+ state->unc = talloc_asprintf(state, "\\\\%s\\%s",
+ state->host, state->share);
+ if (tevent_req_nomem(state->unc, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (existing_conn != NULL) {
+ NTSTATUS status;
+
+ status = smb2_transport_raw_init(state, ev,
+ existing_conn,
+ &state->options,
+ &state->transport);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ smb2_connect_session_start(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+ }
+
+ creq = smbcli_sock_connect_send(state, NULL, state->ports,
+ state->host, state->resolve_ctx,
+ state->ev, state->socket_options,
+ &state->calling,
+ &state->called);
+ if (tevent_req_nomem(creq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ creq->async.fn = smb2_connect_socket_done;
+ creq->async.private_data = req;
+
+ return req;
+}
+
+static void smb2_connect_negprot_done(struct tevent_req *subreq);
+
+static void smb2_connect_socket_done(struct composite_context *creq)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(creq->async.private_data,
+ struct tevent_req);
+ struct smb2_connect_state *state =
+ tevent_req_data(req,
+ struct smb2_connect_state);
+ struct smbcli_socket *sock;
+ struct tevent_req *subreq;
+ NTSTATUS status;
+ uint32_t timeout_msec;
+ enum protocol_types min_protocol;
+
+ status = smbcli_sock_connect_recv(creq, state, &sock);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->transport = smb2_transport_init(sock, state, &state->options);
+ if (tevent_req_nomem(state->transport, req)) {
+ return;
+ }
+
+ timeout_msec = state->transport->options.request_timeout * 1000;
+ min_protocol = state->transport->options.min_protocol;
+ if (min_protocol < PROTOCOL_SMB2_02) {
+ min_protocol = PROTOCOL_SMB2_02;
+ }
+
+ subreq = smbXcli_negprot_send(state, state->ev,
+ state->transport->conn, timeout_msec,
+ min_protocol,
+ state->transport->options.max_protocol,
+ state->transport->options.max_credits,
+ NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, smb2_connect_negprot_done, req);
+}
+
+static void smb2_connect_session_done(struct tevent_req *subreq);
+
+static void smb2_connect_negprot_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ NTSTATUS status;
+
+ status = smbXcli_negprot_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ smb2_connect_session_start(req);
+}
+
+static void smb2_connect_session_start(struct tevent_req *req)
+{
+ struct smb2_connect_state *state =
+ tevent_req_data(req,
+ struct smb2_connect_state);
+ struct smb2_transport *transport = state->transport;
+ struct tevent_req *subreq = NULL;
+
+ state->session = smb2_session_init(transport, state->gensec_settings, state);
+ if (tevent_req_nomem(state->session, req)) {
+ return;
+ }
+
+ if (state->options.only_negprot) {
+ state->tree = smb2_tree_init(state->session, state, true);
+ if (tevent_req_nomem(state->tree, req)) {
+ return;
+ }
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = smb2_session_setup_spnego_send(state, state->ev,
+ state->session,
+ state->credentials,
+ state->previous_session_id);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, smb2_connect_session_done, req);
+}
+
+static void smb2_connect_enc_start(struct tevent_req *req);
+static void smb2_connect_tcon_start(struct tevent_req *req);
+static void smb2_connect_tcon_done(struct tevent_req *subreq);
+
+static void smb2_connect_session_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct smb2_connect_state *state =
+ tevent_req_data(req,
+ struct smb2_connect_state);
+ NTSTATUS status;
+
+ status = smb2_session_setup_spnego_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status) &&
+ !cli_credentials_is_anonymous(state->credentials) &&
+ state->fallback_to_anonymous) {
+ struct cli_credentials *anon_creds = NULL;
+
+ /*
+ * The transport was moved to session,
+ * we need to revert that before removing
+ * the old broken session.
+ */
+ state->transport = talloc_move(state, &state->session->transport);
+ TALLOC_FREE(state->session);
+
+ anon_creds = cli_credentials_init_anon(state);
+ if (tevent_req_nomem(anon_creds, req)) {
+ return;
+ }
+ cli_credentials_set_workstation(anon_creds,
+ cli_credentials_get_workstation(state->credentials),
+ CRED_SPECIFIED);
+
+ /*
+ * retry with anonymous credentials
+ */
+ state->credentials = anon_creds;
+ smb2_connect_session_start(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->tree = smb2_tree_init(state->session, state, true);
+ if (tevent_req_nomem(state->tree, req)) {
+ return;
+ }
+
+ smb2_connect_enc_start(req);
+}
+
+static void smb2_connect_enc_start(struct tevent_req *req)
+{
+ struct smb2_connect_state *state =
+ tevent_req_data(req,
+ struct smb2_connect_state);
+ enum smb_encryption_setting encryption_state =
+ cli_credentials_get_smb_encryption(state->credentials);
+ NTSTATUS status;
+
+ if (encryption_state < SMB_ENCRYPTION_DESIRED) {
+ smb2_connect_tcon_start(req);
+ return;
+ }
+
+ status = smb2cli_session_encryption_on(state->session->smbXcli);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
+ smb2_connect_tcon_start(req);
+ return;
+ }
+
+ DBG_ERR("Encryption required and server doesn't support "
+ "SMB3 encryption - failing connect\n");
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ DBG_ERR("Encryption required and setup failed with error %s.\n",
+ nt_errstr(status));
+ tevent_req_nterror(req, NT_STATUS_PROTOCOL_NOT_SUPPORTED);
+ return;
+ }
+
+ smb2_connect_tcon_start(req);
+}
+
+static void smb2_connect_tcon_start(struct tevent_req *req)
+{
+ struct smb2_connect_state *state =
+ tevent_req_data(req,
+ struct smb2_connect_state);
+ struct tevent_req *subreq = NULL;
+ uint32_t timeout_msec;
+
+ timeout_msec = state->transport->options.request_timeout * 1000;
+
+ subreq = smb2cli_tcon_send(state, state->ev,
+ state->transport->conn,
+ timeout_msec,
+ state->session->smbXcli,
+ state->tree->smbXcli,
+ 0, /* flags */
+ state->unc);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, smb2_connect_tcon_done, req);
+}
+
+static void smb2_connect_tcon_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ NTSTATUS status;
+
+ status = smb2cli_tcon_recv(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS smb2_connect_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb2_tree **tree)
+{
+ struct smb2_connect_state *state =
+ tevent_req_data(req,
+ struct smb2_connect_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *tree = talloc_move(mem_ctx, &state->tree);
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*
+ sync version of smb2_connect
+*/
+NTSTATUS smb2_connect_ext(TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char **ports,
+ const char *share,
+ struct resolve_context *resolve_ctx,
+ struct cli_credentials *credentials,
+ struct smbXcli_conn **existing_conn,
+ uint64_t previous_session_id,
+ struct smb2_tree **tree,
+ struct tevent_context *ev,
+ const struct smbcli_options *options,
+ const char *socket_options,
+ struct gensec_settings *gensec_settings)
+{
+ struct tevent_req *subreq;
+ NTSTATUS status;
+ bool ok;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (frame == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ subreq = smb2_connect_send(frame,
+ ev,
+ host,
+ ports,
+ share,
+ resolve_ctx,
+ credentials,
+ false, /* fallback_to_anonymous */
+ existing_conn,
+ previous_session_id,
+ options,
+ socket_options,
+ gensec_settings);
+ if (subreq == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ status = map_nt_error_from_unix_common(errno);
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = smb2_connect_recv(subreq, mem_ctx, tree);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
+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,
+ const struct smbcli_options *options,
+ const char *socket_options,
+ struct gensec_settings *gensec_settings)
+{
+ NTSTATUS status;
+
+ status = smb2_connect_ext(mem_ctx, host, ports, share, resolve_ctx,
+ credentials,
+ NULL, /* existing_conn */
+ 0, /* previous_session_id */
+ tree, ev, options, socket_options,
+ gensec_settings);
+
+ return status;
+}
diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c
new file mode 100644
index 0000000..c91b150
--- /dev/null
+++ b/source4/libcli/smb2/create.c
@@ -0,0 +1,446 @@
+/*
+ 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"
+
+/*
+ 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_open_v2) {
+ uint8_t data[32];
+ uint32_t flags = 0;
+ struct GUID_ndr_buf guid_buf = { .buf = {0}, };
+
+ SIVAL(data, 0, io->in.timeout);
+ if (io->in.persistent_open) {
+ flags = SMB2_DHANDLE_FLAG_PERSISTENT;
+ }
+ SIVAL(data, 4, flags);
+ SBVAL(data, 8, 0x0); /* reserved */
+ status = GUID_to_ndr_buf(&io->in.create_guid, &guid_buf);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ memcpy(data+16, guid_buf.buf, sizeof(guid_buf.buf));
+
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_DH2Q,
+ data_blob_const(data, 32));
+ 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.durable_handle_v2) {
+ uint8_t data[36];
+ struct GUID_ndr_buf guid_buf = { .buf = {0}, };
+ uint32_t flags = 0;
+
+ smb2_push_handle(data, io->in.durable_handle_v2);
+ status = GUID_to_ndr_buf(&io->in.create_guid, &guid_buf);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ memcpy(data+16, guid_buf.buf, sizeof(guid_buf.buf));
+ if (io->in.persistent_open) {
+ flags = SMB2_DHANDLE_FLAG_PERSISTENT;
+ }
+ SIVAL(data, 32, flags);
+
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_DH2C,
+ data_blob_const(data, 36));
+ 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, 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;
+ }
+ }
+
+ if (io->in.lease_request) {
+ uint8_t data[32];
+
+ if (!smb2_lease_push(io->in.lease_request, data,
+ sizeof(data))) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ status = smb2_create_blob_add(
+ req, &blobs, SMB2_CREATE_TAG_RQLS,
+ data_blob_const(data, sizeof(data)));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.lease_request_v2) {
+ uint8_t data[52];
+
+ if (!smb2_lease_push(io->in.lease_request_v2, data,
+ sizeof(data))) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ status = smb2_create_blob_add(
+ req, &blobs, SMB2_CREATE_TAG_RQLS,
+ data_blob_const(data, sizeof(data)));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.app_instance_id) {
+ uint8_t data[20];
+ struct GUID_ndr_buf guid_buf = { .buf = {0}, };
+
+ SSVAL(data, 0, 20); /* structure size */
+ SSVAL(data, 2, 0); /* reserved */
+
+ status = GUID_to_ndr_buf(io->in.app_instance_id, &guid_buf);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ memcpy(data+4, guid_buf.buf, sizeof(guid_buf.buf));
+
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_APP_INSTANCE_ID,
+ data_blob_const(data, 20));
+ 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;
+ }
+
+ if (((io->in.fname == NULL) || (strlen(io->in.fname) == 0)) &&
+ (blob.length == 0)) {
+ struct smb2_request_buffer *buf = &req->out;
+
+ status = smb2_grow_buffer(buf, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ buf->dynamic[0] = 0;
+ buf->dynamic += 1;
+ buf->body_size += 1;
+ buf->size += 1;
+ }
+
+ 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) {
+ if (io->out.blobs.blobs[i].data.length != 8) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ io->out.maximal_access_status =
+ IVAL(io->out.blobs.blobs[i].data.data, 0);
+ 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);
+ }
+ if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_RQLS) == 0) {
+ struct smb2_lease *ls = NULL;
+ uint8_t *data;
+
+ ZERO_STRUCT(io->out.lease_response);
+ ZERO_STRUCT(io->out.lease_response_v2);
+
+ switch (io->out.blobs.blobs[i].data.length) {
+ case 32:
+ ls = &io->out.lease_response;
+ ls->lease_version = 1;
+ break;
+ case 52:
+ ls = &io->out.lease_response_v2;
+ ls->lease_version = 2;
+ break;
+ default:
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ data = io->out.blobs.blobs[i].data.data;
+ memcpy(&ls->lease_key, data, 16);
+ ls->lease_state = IVAL(data, 16);
+ ls->lease_flags = IVAL(data, 20);
+ ls->lease_duration = BVAL(data, 24);
+
+ if (io->out.blobs.blobs[i].data.length == 52) {
+ memcpy(&ls->parent_lease_key, data+32, 16);
+ ls->lease_epoch = SVAL(data, 48);
+ }
+ }
+ if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_DHNQ) == 0) {
+ if (io->out.blobs.blobs[i].data.length != 8) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ io->out.durable_open = true;
+ }
+ if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_DH2Q) == 0) {
+ uint32_t flags;
+ uint8_t *data;
+
+ if (io->out.blobs.blobs[i].data.length != 8) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ io->out.durable_open = false;
+ io->out.durable_open_v2 = true;
+
+ data = io->out.blobs.blobs[i].data.data;
+ io->out.timeout = IVAL(data, 0);
+ flags = IVAL(data, 4);
+ if ((flags & SMB2_DHANDLE_FLAG_PERSISTENT) != 0) {
+ io->out.persistent_open = true;
+ }
+ }
+ }
+
+ 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 0000000..23ac737
--- /dev/null
+++ b/source4/libcli/smb2/find.c
@@ -0,0 +1,181 @@
+/*
+ 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_QUERY_DIRECTORY, 0x20, true, 0);
+ if (req == NULL) return NULL;
+ req->credit_charge = (MAX(io->in.max_response_size, 1) - 1)/ 65536 + 1;
+
+ 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, unsigned int *count,
+ union smb_search_data **io)
+{
+ struct smb2_find f;
+ NTSTATUS status;
+ DATA_BLOB b;
+ enum smb_search_data_level smb_level;
+ unsigned int 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,
+ unsigned int *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 0000000..577d1ba
--- /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 0000000..5ffb988
--- /dev/null
+++ b/source4/libcli/smb2/getinfo.c
@@ -0,0 +1,239 @@
+/*
+ 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;
+ size_t max_payload;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_GETINFO, 0x28, true,
+ io->in.input_buffer.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);
+ /*
+ * uint16_t input_buffer_offset
+ * uint16_t reserved
+ * uint32_t input_buffer_length
+ *
+ * We use smb2_push_o32s32_blob() which would
+ * expect uint32_t offset, uint32_t length.
+ *
+ * Everything is little endian, we can just
+ * overwrite the reserved field later.
+ */
+ 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.input_buffer);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ SSVAL(req->out.body, 0x0C, io->in.reserved);
+
+ max_payload = MAX(io->in.output_buffer_length, io->in.input_buffer.length);
+ req->credit_charge = (MAX(max_payload, 1) - 1)/ 65536 + 1;
+
+ 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_0_INFO_FILE &&
+ level == RAW_FILEINFO_SEC_DESC) {
+ return SMB2_0_INFO_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_0_INFO_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 = data_blob_null;
+ 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_0_INFO_FILESYSTEM);
+
+ 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 = {
+ .in = {0},
+ };
+ 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 0000000..fe74dfe
--- /dev/null
+++ b/source4/libcli/smb2/ioctl.c
@@ -0,0 +1,151 @@
+/*
+ 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"
+#include "librpc/gen_ndr/ioctl.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;
+ uint64_t max_payload_in;
+ uint64_t max_payload_out;
+ size_t max_payload;
+
+ 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.max_input_response);
+
+ 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_output_response);
+ SBVAL(req->out.body, 0x30, io->in.flags);
+
+ max_payload_in = io->in.out.length + io->in.in.length;
+ max_payload_in = MIN(max_payload_in, UINT32_MAX);
+ max_payload_out = io->in.max_input_response + io->in.max_output_response;
+ max_payload_out = MIN(max_payload_out, UINT32_MAX);
+
+ max_payload = MAX(max_payload_in, max_payload_out);
+ req->credit_charge = (MAX(max_payload, 1) - 1)/ 65536 + 1;
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+/*
+ * 3.3.4.4 Sending an Error Response
+ */
+static bool smb2_ioctl_is_failure(uint32_t ctl_code, NTSTATUS status,
+ size_t data_size)
+{
+ if (NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)
+ && ((ctl_code == FSCTL_PIPE_TRANSCEIVE)
+ || (ctl_code == FSCTL_PIPE_PEEK)
+ || (ctl_code == FSCTL_DFS_GET_REFERRALS))) {
+ return false;
+ }
+
+ if (((ctl_code == FSCTL_SRV_COPYCHUNK)
+ || (ctl_code == FSCTL_SRV_COPYCHUNK_WRITE))
+ && (data_size == sizeof(struct srv_copychunk_rsp))) {
+ /*
+ * copychunk responses may come with copychunk data or error
+ * response data, independent of status.
+ */
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ 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_ioctl_is_failure(io->in.function, req->status,
+ req->in.bufinfo.data_size)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x30, true);
+
+ io->out.reserved = 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.flags = IVAL(req->in.body, 0x28);
+ io->out.reserved2 = 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 0000000..71004aa
--- /dev/null
+++ b/source4/libcli/smb2/keepalive.c
@@ -0,0 +1,68 @@
+/*
+ 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_session *session)
+{
+ 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);
+
+ req->session = session;
+
+ 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, NULL);
+ return smb2_keepalive_recv(req);
+}
diff --git a/source4/libcli/smb2/lease_break.c b/source4/libcli/smb2/lease_break.c
new file mode 100644
index 0000000..c238f1d
--- /dev/null
+++ b/source4/libcli/smb2/lease_break.c
@@ -0,0 +1,81 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client oplock break handling
+
+ Copyright (C) Zachary Loafman 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ Send a Lease Break Acknowledgement
+*/
+struct smb2_request *smb2_lease_break_ack_send(struct smb2_tree *tree,
+ struct smb2_lease_break_ack *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_BREAK, 0x24, false, 0);
+ if (req == NULL) return NULL;
+
+ SIVAL(req->out.body, 0x02, io->in.reserved);
+ SIVAL(req->out.body, 0x04, io->in.lease.lease_flags);
+ memcpy(req->out.body+0x8, &io->in.lease.lease_key,
+ sizeof(struct smb2_lease_key));
+ SIVAL(req->out.body, 0x18, io->in.lease.lease_state);
+ SBVAL(req->out.body, 0x1C, io->in.lease.lease_duration);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ Receive a Lease Break Response
+*/
+NTSTATUS smb2_lease_break_ack_recv(struct smb2_request *req,
+ struct smb2_lease_break_ack *io)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x24, false);
+
+ io->out.reserved = IVAL(req->in.body, 0x02);
+ io->out.lease.lease_flags = IVAL(req->in.body, 0x04);
+ memcpy(&io->out.lease.lease_key, req->in.body+0x8,
+ sizeof(struct smb2_lease_key));
+ io->out.lease.lease_state = IVAL(req->in.body, 0x18);
+ io->out.lease.lease_duration = IVAL(req->in.body, 0x1C);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync flush request
+*/
+NTSTATUS smb2_lease_break_ack(struct smb2_tree *tree,
+ struct smb2_lease_break_ack *io)
+{
+ struct smb2_request *req = smb2_lease_break_ack_send(tree, io);
+ return smb2_lease_break_ack_recv(req, io);
+}
diff --git a/source4/libcli/smb2/lock.c b/source4/libcli/smb2/lock.c
new file mode 100644
index 0000000..f2a76d8
--- /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.lock_sequence);
+ 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 0000000..12cd553
--- /dev/null
+++ b/source4/libcli/smb2/logoff.c
@@ -0,0 +1,67 @@
+/*
+ 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;
+
+ 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/notify.c b/source4/libcli/smb2/notify.c
new file mode 100644
index 0000000..6786a70
--- /dev/null
+++ b/source4/libcli/smb2/notify.c
@@ -0,0 +1,116 @@
+/*
+ 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);
+
+ req->credit_charge = (MAX(io->in.buffer_size, 1) - 1)/ 65536 + 1;
+
+ 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 0000000..ca487a7
--- /dev/null
+++ b/source4/libcli/smb2/read.c
@@ -0,0 +1,89 @@
+/*
+ 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);
+
+ req->credit_charge = (MAX(io->in.length, 1) - 1)/ 65536 + 1;
+
+ 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 0000000..3024e01
--- /dev/null
+++ b/source4/libcli/smb2/request.c
@@ -0,0 +1,717 @@
+/*
+ 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;
+ }
+}
+
+/*
+ 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;
+ uint32_t hdr_offset;
+ bool compound = false;
+
+ if (body_dynamic_present) {
+ if (body_dynamic_size == 0) {
+ body_dynamic_size = 1;
+ }
+ } else {
+ body_dynamic_size = 0;
+ }
+
+ req = talloc_zero(transport, struct smb2_request);
+ if (req == NULL) return NULL;
+
+ req->state = SMB2_REQUEST_INIT;
+ req->transport = transport;
+
+ hdr_offset = NBT_HDR_SIZE;
+
+ req->out.size = hdr_offset + SMB2_HDR_BODY + body_fixed_size;
+ req->out.allocated = req->out.size + body_dynamic_size;
+
+ req->out.buffer = talloc_realloc(req, req->out.buffer,
+ uint8_t, req->out.allocated);
+ if (req->out.buffer == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ req->out.hdr = req->out.buffer + hdr_offset;
+ 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_CREDIT_CHARGE, 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, 0);
+ 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 && !compound) {
+ req->out.size += 1;
+ SCVAL(req->out.dynamic, 0, 0);
+ }
+
+ 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;
+
+ 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 (tevent_loop_once(req->transport->ev) != 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)) {
+ if (buf->dynamic != (buf->buffer + buf->size)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ grow a SMB2 buffer by the specified amount
+*/
+NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase)
+{
+ size_t hdr_ofs;
+ 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;
+
+ hdr_ofs = buf->hdr - buf->buffer;
+ 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 + hdr_ofs;
+ 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;
+ size_t converted_size = 0;
+ 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, &converted_size);
+ 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;
+ void *ptr = NULL;
+
+ 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), &ptr, &blob.length);
+ if (!ret) {
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+ blob.data = (uint8_t *)ptr;
+
+ 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 0000000..e94512d
--- /dev/null
+++ b/source4/libcli/smb2/session.c
@@ -0,0 +1,477 @@
+/*
+ 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 "system/network.h"
+#include <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+/**
+ initialise a smb2_session structure
+ */
+struct smb2_session *smb2_session_init(struct smb2_transport *transport,
+ struct gensec_settings *settings,
+ TALLOC_CTX *parent_ctx)
+{
+ struct smb2_session *session;
+ NTSTATUS status;
+
+ session = talloc_zero(parent_ctx, struct smb2_session);
+ if (!session) {
+ return NULL;
+ }
+ session->transport = talloc_steal(session, transport);
+
+ session->smbXcli = smbXcli_session_create(session, transport->conn);
+ if (session->smbXcli == NULL) {
+ talloc_free(session);
+ return NULL;
+ }
+
+ /* prepare a gensec context for later use */
+ status = gensec_client_start(session, &session->gensec,
+ settings);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(session);
+ return NULL;
+ }
+
+ gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
+
+ return session;
+}
+
+/*
+ * Note: that the caller needs to keep 'transport' around as
+ * long as the returned session is active!
+ */
+struct smb2_session *smb2_session_channel(struct smb2_transport *transport,
+ struct gensec_settings *settings,
+ TALLOC_CTX *parent_ctx,
+ struct smb2_session *base_session)
+{
+ struct smb2_session *session;
+ NTSTATUS status;
+
+ session = talloc_zero(parent_ctx, struct smb2_session);
+ if (!session) {
+ return NULL;
+ }
+ session->transport = transport;
+
+ status = smb2cli_session_create_channel(session,
+ base_session->smbXcli,
+ transport->conn,
+ &session->smbXcli);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(session);
+ return NULL;
+ }
+
+ session->needs_bind = true;
+
+ /* prepare a gensec context for later use */
+ status = gensec_client_start(session, &session->gensec,
+ settings);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(session);
+ return NULL;
+ }
+
+ gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
+
+ return session;
+}
+
+struct smb2_session_setup_spnego_state {
+ struct tevent_context *ev;
+ struct smb2_session *session;
+ struct cli_credentials *credentials;
+ uint64_t previous_session_id;
+ bool session_bind;
+ bool reauth;
+ NTSTATUS gensec_status;
+ NTSTATUS remote_status;
+ DATA_BLOB in_secblob;
+ DATA_BLOB out_secblob;
+ struct iovec *recv_iov;
+};
+
+static void smb2_session_setup_spnego_gensec_next(struct tevent_req *req);
+static void smb2_session_setup_spnego_gensec_done(struct tevent_req *subreq);
+static void smb2_session_setup_spnego_smb2_next(struct tevent_req *req);
+static void smb2_session_setup_spnego_smb2_done(struct tevent_req *subreq);
+static void smb2_session_setup_spnego_both_ready(struct tevent_req *req);
+
+/*
+ a composite function that does a full SPNEGO session setup
+ */
+struct tevent_req *smb2_session_setup_spnego_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smb2_session *session,
+ struct cli_credentials *credentials,
+ uint64_t previous_session_id)
+{
+ struct smb2_transport *transport = session->transport;
+ struct tevent_req *req;
+ struct smb2_session_setup_spnego_state *state;
+ uint64_t current_session_id;
+ const char *chosen_oid;
+ NTSTATUS status;
+ const DATA_BLOB *server_gss_blob;
+ struct timeval endtime;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct smb2_session_setup_spnego_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->session = session;
+ state->credentials = credentials;
+ state->previous_session_id = previous_session_id;
+ state->gensec_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+ state->remote_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+
+ endtime = timeval_current_ofs(transport->options.request_timeout, 0);
+
+ ok = tevent_req_set_endtime(req, ev, endtime);
+ if (!ok) {
+ return tevent_req_post(req, ev);
+ }
+
+ current_session_id = smb2cli_session_current_id(state->session->smbXcli);
+ if (state->session->needs_bind) {
+ state->session_bind = true;
+ } else if (current_session_id != 0) {
+ state->reauth = true;
+ }
+ server_gss_blob = smbXcli_conn_server_gss_blob(session->transport->conn);
+ if (server_gss_blob) {
+ state->out_secblob = *server_gss_blob;
+ }
+
+ status = gensec_set_credentials(session->gensec, credentials);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = gensec_set_target_hostname(session->gensec,
+ smbXcli_conn_remote_name(session->transport->conn));
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = gensec_set_target_service(session->gensec, "cifs");
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (state->out_secblob.length > 0) {
+ 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)));
+ state->out_secblob = data_blob_null;
+ 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)));
+ }
+ }
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ 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 (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ smb2_session_setup_spnego_gensec_next(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void smb2_session_setup_spnego_gensec_next(struct tevent_req *req)
+{
+ struct smb2_session_setup_spnego_state *state =
+ tevent_req_data(req,
+ struct smb2_session_setup_spnego_state);
+ struct smb2_session *session = state->session;
+ struct tevent_req *subreq = NULL;
+
+ if (NT_STATUS_IS_OK(state->gensec_status)) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ subreq = gensec_update_send(state, state->ev,
+ session->gensec,
+ state->out_secblob);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ smb2_session_setup_spnego_gensec_done,
+ req);
+}
+
+static void smb2_session_setup_spnego_gensec_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct smb2_session_setup_spnego_state *state =
+ tevent_req_data(req,
+ struct smb2_session_setup_spnego_state);
+ NTSTATUS status;
+
+ status = gensec_update_recv(subreq, state,
+ &state->in_secblob);
+ state->gensec_status = status;
+ state->out_secblob = data_blob_null;
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (NT_STATUS_IS_OK(state->remote_status) &&
+ NT_STATUS_IS_OK(state->gensec_status)) {
+ smb2_session_setup_spnego_both_ready(req);
+ return;
+ }
+
+ smb2_session_setup_spnego_smb2_next(req);
+}
+
+static void smb2_session_setup_spnego_smb2_next(struct tevent_req *req)
+{
+ struct smb2_session_setup_spnego_state *state =
+ tevent_req_data(req,
+ struct smb2_session_setup_spnego_state);
+ struct smb2_session *session = state->session;
+ uint32_t timeout_msec;
+ uint8_t in_flags = 0;
+ struct tevent_req *subreq = NULL;
+
+ if (NT_STATUS_IS_OK(state->remote_status)) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ timeout_msec = session->transport->options.request_timeout * 1000;
+
+ if (state->session_bind) {
+ in_flags |= SMB2_SESSION_FLAG_BINDING;
+ }
+
+ subreq = smb2cli_session_setup_send(state, state->ev,
+ session->transport->conn,
+ timeout_msec,
+ session->smbXcli,
+ in_flags,
+ 0, /* in_capabilities */
+ 0, /* in_channel */
+ state->previous_session_id,
+ &state->in_secblob);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ smb2_session_setup_spnego_smb2_done,
+ req);
+}
+
+/*
+ handle continuations of the spnego session setup
+*/
+static void smb2_session_setup_spnego_smb2_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct smb2_session_setup_spnego_state *state =
+ tevent_req_data(req,
+ struct smb2_session_setup_spnego_state);
+ NTSTATUS status;
+
+ status = smb2cli_session_setup_recv(subreq, state,
+ &state->recv_iov,
+ &state->out_secblob);
+ state->remote_status = status;
+ state->in_secblob = data_blob_null;
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (NT_STATUS_IS_OK(state->remote_status) &&
+ NT_STATUS_IS_OK(state->gensec_status)) {
+ smb2_session_setup_spnego_both_ready(req);
+ return;
+ }
+
+ smb2_session_setup_spnego_gensec_next(req);
+}
+
+static void smb2_session_setup_spnego_both_ready(struct tevent_req *req)
+{
+ struct smb2_session_setup_spnego_state *state =
+ tevent_req_data(req,
+ struct smb2_session_setup_spnego_state);
+ struct smb2_session *session = state->session;
+ NTSTATUS status;
+ DATA_BLOB session_key;
+
+ if (state->out_secblob.length != 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (state->in_secblob.length != 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (state->reauth) {
+ tevent_req_done(req);
+ return;
+ }
+
+ if (cli_credentials_is_anonymous(state->credentials)) {
+ /*
+ * Windows server does not set the
+ * SMB2_SESSION_FLAG_IS_GUEST nor
+ * SMB2_SESSION_FLAG_IS_NULL flag.
+ *
+ * This fix makes sure we do not try
+ * to verify a signature on the final
+ * session setup response.
+ */
+ tevent_req_done(req);
+ return;
+ }
+
+ status = gensec_session_key(session->gensec, state,
+ &session_key);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->session_bind) {
+ status = smb2cli_session_set_channel_key(session->smbXcli,
+ session_key,
+ state->recv_iov);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ session->needs_bind = false;
+ } else {
+ status = smb2cli_session_set_session_key(session->smbXcli,
+ session_key,
+ state->recv_iov);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ }
+ tevent_req_done(req);
+ return;
+}
+
+/*
+ receive a composite session setup reply
+*/
+NTSTATUS smb2_session_setup_spnego_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ sync version of smb2_session_setup_spnego
+*/
+NTSTATUS smb2_session_setup_spnego(struct smb2_session *session,
+ struct cli_credentials *credentials,
+ uint64_t previous_session_id)
+{
+ struct tevent_req *subreq;
+ NTSTATUS status;
+ bool ok;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = session->transport->ev;
+
+ if (frame == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ subreq = smb2_session_setup_spnego_send(frame, ev,
+ session, credentials,
+ previous_session_id);
+ if (subreq == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ status = map_nt_error_from_unix_common(errno);
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = smb2_session_setup_spnego_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb2/setinfo.c b/source4/libcli/smb2/setinfo.c
new file mode 100644
index 0000000..f8b50f6
--- /dev/null
+++ b/source4/libcli/smb2/setinfo.c
@@ -0,0 +1,123 @@
+/*
+ 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_0_INFO_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 0000000..c423b7c
--- /dev/null
+++ b/source4/libcli/smb2/signing.c
@@ -0,0 +1,139 @@
+/*
+ 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 <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include "lib/crypto/gnutls_helpers.h"
+
+/*
+ sign an outgoing message
+ */
+NTSTATUS smb2_sign_message(struct smb2_request_buffer *buf, DATA_BLOB session_key)
+{
+ uint8_t digest[gnutls_hash_get_len(GNUTLS_MAC_SHA256)];
+ uint64_t session_id;
+ size_t hdr_offset;
+ int rc;
+
+ if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+ /* can't sign non-SMB2 messages */
+ return NT_STATUS_OK;
+ }
+
+ hdr_offset = buf->hdr - buf->buffer;
+
+ 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);
+
+ rc = gnutls_hmac_fast(GNUTLS_MAC_SHA256,
+ session_key.data,
+ MIN(session_key.length, 16),
+ buf->hdr,
+ buf->size - hdr_offset,
+ digest);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED);
+ }
+
+ DEBUG(5,("signed SMB2 message of size %u\n", (unsigned)buf->size - NBT_HDR_SIZE));
+
+ memcpy(buf->hdr + SMB2_HDR_SIGNATURE, digest, 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;
+ uint8_t digest[gnutls_hash_get_len(GNUTLS_MAC_SHA256)];
+ uint8_t sig[16];
+ size_t hdr_offset;
+ int rc;
+
+ if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+ /* can't check non-SMB2 messages */
+ return NT_STATUS_OK;
+ }
+
+ hdr_offset = buf->hdr - buf->buffer;
+
+ 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);
+
+ rc = gnutls_hmac_fast(GNUTLS_MAC_SHA256,
+ session_key.data,
+ MIN(session_key.length, 16),
+ buf->hdr,
+ buf->size - hdr_offset,
+ digest);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED);
+ }
+
+ memcpy(buf->hdr + SMB2_HDR_SIGNATURE, digest, 16);
+
+ if (!mem_equal_const_time(digest, sig, 16)) {
+ 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, digest, 16);
+ ZERO_ARRAY(digest);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ ZERO_ARRAY(digest);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h
new file mode 100644
index 0000000..4aadab2
--- /dev/null
+++ b/source4/libcli/smb2/smb2.h
@@ -0,0 +1,204 @@
+/*
+ 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;
+struct smb2_lease_break;
+
+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;
+};
+
+/* this is the context for the smb2 transport layer */
+struct smb2_transport {
+ struct tevent_context *ev; /* TODO: remove this !!! */
+ struct smbXcli_conn *conn;
+
+ /* the details for coumpounded requests */
+ struct {
+ bool related;
+ struct tevent_req **reqs;
+ } compound;
+
+ /* 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;
+ unsigned int period;
+ struct tevent_timer *te;
+ } 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 {
+ /* a lease break request handler */
+ bool (*handler)(struct smb2_transport *transport,
+ const struct smb2_lease_break *lease_break,
+ void *private_data);
+ /* private data passed to the oplock handler */
+ void *private_data;
+ } lease;
+ struct tevent_req *break_subreq;
+
+ struct smbcli_options options;
+};
+
+
+/*
+ SMB2 LSA state
+*/
+struct smb2lsa_state {
+ struct dcerpc_binding_handle *binding_handle;
+ struct smb2_tree *ipc_tree;
+ struct policy_handle handle;
+};
+
+
+/*
+ SMB2 tree context
+*/
+struct smb2_tree {
+ struct smb2_session *session;
+ struct smbXcli_tcon *smbXcli;
+ struct smb2lsa_state *lsa;
+};
+
+/*
+ SMB2 session context
+*/
+struct smb2_session {
+ struct smb2_transport *transport;
+ struct gensec_security *gensec;
+ struct smbXcli_session *smbXcli;
+ bool needs_bind;
+};
+
+
+
+/*
+ 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 {
+ /* each request is in one of 3 possible states */
+ enum smb2_request_state state;
+
+ struct tevent_req *subreq;
+
+ struct smb2_transport *transport;
+ struct smb2_session *session;
+ struct smb2_tree *tree;
+
+ struct {
+ bool can_cancel;
+ } 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;
+ struct iovec *recv_iov;
+
+ uint16_t credit_charge;
+
+ /* 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
+
+/*
+ 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 0000000..b6c08c2
--- /dev/null
+++ b/source4/libcli/smb2/smb2_calls.h
@@ -0,0 +1,99 @@
+/*
+ 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;
+};
+
+/* 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;
+ /* uint16_t input_buffer_offset; */
+ uint16_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 input_buffer;
+ } 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 0000000..702e308
--- /dev/null
+++ b/source4/libcli/smb2/tcon.c
@@ -0,0 +1,52 @@
+/*
+ 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"
+#include "../libcli/smb/smbXcli_base.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);
+ }
+
+ tree->smbXcli = smbXcli_tcon_create(tree);
+ if (tree->smbXcli == NULL) {
+ talloc_free(tree);
+ return NULL;
+ }
+
+ return tree;
+}
diff --git a/source4/libcli/smb2/tdis.c b/source4/libcli/smb2/tdis.c
new file mode 100644
index 0000000..5adad9d
--- /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 0000000..292ca0f
--- /dev/null
+++ b/source4/libcli/smb2/transport.c
@@ -0,0 +1,557 @@
+/*
+ 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 "system/network.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/util/dlinklist.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "librpc/ndr/libndr.h"
+
+/*
+ destroy a transport
+ */
+static int transport_destructor(struct smb2_transport *transport)
+{
+ smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
+ return 0;
+}
+
+/*
+ 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->ev = sock->event.ctx;
+ transport->options = *options;
+
+ if (transport->options.max_protocol == PROTOCOL_DEFAULT) {
+ transport->options.max_protocol = PROTOCOL_LATEST;
+ }
+
+ if (transport->options.max_protocol < PROTOCOL_SMB2_02) {
+ transport->options.max_protocol = PROTOCOL_LATEST;
+ }
+
+ TALLOC_FREE(sock->event.fde);
+ TALLOC_FREE(sock->event.te);
+
+ transport->conn = smbXcli_conn_create(transport,
+ sock->sock->fd,
+ sock->hostname,
+ options->signing,
+ 0, /* smb1_capabilities */
+ &options->client_guid,
+ options->smb2_capabilities,
+ &options->smb3_capabilities);
+ if (transport->conn == NULL) {
+ talloc_free(transport);
+ return NULL;
+ }
+ sock->sock->fd = -1;
+ TALLOC_FREE(sock);
+
+ talloc_set_destructor(transport, transport_destructor);
+
+ return transport;
+}
+
+/*
+ create a transport structure based on an established socket
+*/
+NTSTATUS smb2_transport_raw_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smbXcli_conn **_conn,
+ const struct smbcli_options *options,
+ struct smb2_transport **_transport)
+{
+ struct smb2_transport *transport = NULL;
+ enum protocol_types protocol;
+
+ if (*_conn == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ protocol = smbXcli_conn_protocol(*_conn);
+ if (protocol < PROTOCOL_SMB2_02) {
+ return NT_STATUS_REVISION_MISMATCH;
+ }
+
+ transport = talloc_zero(mem_ctx, struct smb2_transport);
+ if (transport == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ transport->ev = ev;
+ transport->options = *options;
+ transport->conn = talloc_move(transport, _conn);
+
+ talloc_set_destructor(transport, transport_destructor);
+ *_transport = transport;
+ return NT_STATUS_OK;
+}
+
+/*
+ mark the transport as dead
+*/
+void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
+{
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_LOCAL_DISCONNECT;
+ }
+
+ smbXcli_conn_disconnect(transport->conn, status);
+}
+
+static void smb2_request_done(struct tevent_req *subreq);
+static void smb2_transport_break_handler(struct tevent_req *subreq);
+
+/*
+ put a request into the send queue
+*/
+void smb2_transport_send(struct smb2_request *req)
+{
+ NTSTATUS status;
+ struct smb2_transport *transport = req->transport;
+ struct tevent_req **reqs = transport->compound.reqs;
+ size_t num_reqs = talloc_array_length(reqs);
+ size_t i;
+ uint16_t cmd = SVAL(req->out.hdr, SMB2_HDR_OPCODE);
+ uint32_t additional_flags = IVAL(req->out.hdr, SMB2_HDR_FLAGS);
+ uint32_t clear_flags = 0;
+ struct smbXcli_tcon *tcon = NULL;
+ struct smbXcli_session *session = NULL;
+ bool need_pending_break = false;
+ size_t hdr_ofs;
+ size_t pdu_len;
+ DATA_BLOB body = data_blob_null;
+ DATA_BLOB dyn = data_blob_null;
+ uint32_t timeout_msec = transport->options.request_timeout * 1000;
+
+ if (transport->oplock.handler) {
+ need_pending_break = true;
+ }
+
+ if (transport->lease.handler) {
+ need_pending_break = true;
+ }
+
+ if (transport->break_subreq) {
+ need_pending_break = false;
+ }
+
+ if (need_pending_break) {
+ struct tevent_req *subreq;
+
+ subreq = smb2cli_req_create(transport,
+ transport->ev,
+ transport->conn,
+ SMB2_OP_BREAK,
+ 0, /* additional_flags */
+ 0, /*clear_flags */
+ 0, /* timeout_msec */
+ NULL, /* tcon */
+ NULL, /* session */
+ NULL, /* body */
+ 0, /* body_fixed */
+ NULL, /* dyn */
+ 0, /* dyn_len */
+ 0); /* max_dyn_len */
+ if (subreq != NULL) {
+ smbXcli_req_set_pending(subreq);
+ tevent_req_set_callback(subreq,
+ smb2_transport_break_handler,
+ transport);
+ transport->break_subreq = subreq;
+ }
+ }
+
+ if (req->session) {
+ session = req->session->smbXcli;
+ }
+
+ if (req->tree) {
+ tcon = req->tree->smbXcli;
+ }
+
+ if (transport->compound.related) {
+ additional_flags |= SMB2_HDR_FLAG_CHAINED;
+ }
+
+ hdr_ofs = PTR_DIFF(req->out.hdr, req->out.buffer);
+ pdu_len = req->out.size - hdr_ofs;
+ body.data = req->out.body;
+ body.length = req->out.body_fixed;
+ dyn.data = req->out.body + req->out.body_fixed;
+ dyn.length = pdu_len - (SMB2_HDR_BODY + req->out.body_fixed);
+
+ req->subreq = smb2cli_req_create(req,
+ transport->ev,
+ transport->conn,
+ cmd,
+ additional_flags,
+ clear_flags,
+ timeout_msec,
+ tcon,
+ session,
+ body.data, body.length,
+ dyn.data, dyn.length,
+ 0); /* max_dyn_len */
+ if (req->subreq == NULL) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ if (!tevent_req_is_in_progress(req->subreq)) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = NT_STATUS_INTERNAL_ERROR;/* TODO */
+ return;
+ }
+
+ tevent_req_set_callback(req->subreq, smb2_request_done, req);
+
+ smb2cli_req_set_notify_async(req->subreq);
+ if (req->credit_charge) {
+ smb2cli_req_set_credit_charge(req->subreq, req->credit_charge);
+ }
+
+ ZERO_STRUCT(req->out);
+ req->state = SMB2_REQUEST_RECV;
+
+ if (num_reqs > 0) {
+ for (i=0; i < num_reqs; i++) {
+ if (reqs[i] != NULL) {
+ continue;
+ }
+
+ reqs[i] = req->subreq;
+ i++;
+ break;
+ }
+
+ if (i < num_reqs) {
+ return;
+ }
+ } else {
+ reqs = &req->subreq;
+ num_reqs = 1;
+ }
+ status = smb2cli_req_compound_submit(reqs, num_reqs);
+
+ TALLOC_FREE(transport->compound.reqs);
+ transport->compound.related = false;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ req->status = status;
+ req->state = SMB2_REQUEST_ERROR;
+ smbXcli_conn_disconnect(transport->conn, status);
+ }
+}
+
+static void smb2_request_done(struct tevent_req *subreq)
+{
+ struct smb2_request *req =
+ tevent_req_callback_data(subreq,
+ struct smb2_request);
+ ssize_t len;
+ size_t i;
+
+ req->recv_iov = NULL;
+
+ req->status = smb2cli_req_recv(req->subreq, req, &req->recv_iov, NULL, 0);
+ if (NT_STATUS_EQUAL(req->status, NT_STATUS_PENDING)) {
+ struct timeval endtime = smbXcli_req_endtime(subreq);
+ bool ok;
+
+ req->cancel.can_cancel = true;
+ if (timeval_is_zero(&endtime)) {
+ return;
+ }
+
+ ok = tevent_req_set_endtime(
+ subreq, req->transport->ev, endtime);
+ if (!ok) {
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+ return;
+ }
+ TALLOC_FREE(req->subreq);
+ if (!NT_STATUS_IS_OK(req->status)) {
+ if (req->recv_iov == NULL) {
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+ }
+
+ len = req->recv_iov[0].iov_len;
+ for (i=1; i < 3; i++) {
+ uint8_t *p = req->recv_iov[i-1].iov_base;
+ uint8_t *c1 = req->recv_iov[i].iov_base;
+ uint8_t *c2 = p + req->recv_iov[i-1].iov_len;
+
+ len += req->recv_iov[i].iov_len;
+
+ if (req->recv_iov[i].iov_len == 0) {
+ continue;
+ }
+
+ if (c1 != c2) {
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+ }
+
+ req->in.buffer = req->recv_iov[0].iov_base;
+ req->in.size = len;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = req->recv_iov[0].iov_base;
+ req->in.body = req->recv_iov[1].iov_base;
+ req->in.dynamic = req->recv_iov[2].iov_base;
+ req->in.body_fixed = req->recv_iov[1].iov_len;
+ req->in.body_size = req->in.body_fixed;
+ req->in.body_size += req->recv_iov[2].iov_len;
+
+ smb2_setup_bufinfo(req);
+
+ req->state = SMB2_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+static void smb2_transport_break_handler(struct tevent_req *subreq)
+{
+ struct smb2_transport *transport =
+ tevent_req_callback_data(subreq,
+ struct smb2_transport);
+ NTSTATUS status;
+ uint8_t *body;
+ uint16_t len = 0;
+ bool lease;
+ struct iovec *recv_iov = NULL;
+
+ transport->break_subreq = NULL;
+
+ status = smb2cli_req_recv(subreq, transport, &recv_iov, NULL, 0);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(recv_iov);
+ smb2_transport_dead(transport, status);
+ return;
+ }
+
+ /*
+ * Setup the subreq to handle the
+ * next incoming SMB2 Break.
+ */
+ subreq = smb2cli_req_create(transport,
+ transport->ev,
+ transport->conn,
+ SMB2_OP_BREAK,
+ 0, /* additional_flags */
+ 0, /*clear_flags */
+ 0, /* timeout_msec */
+ NULL, /* tcon */
+ NULL, /* session */
+ NULL, /* body */
+ 0, /* body_fixed */
+ NULL, /* dyn */
+ 0, /* dyn_len */
+ 0); /* max_dyn_len */
+ if (subreq != NULL) {
+ smbXcli_req_set_pending(subreq);
+ tevent_req_set_callback(subreq,
+ smb2_transport_break_handler,
+ transport);
+ transport->break_subreq = subreq;
+ }
+
+ body = recv_iov[1].iov_base;
+
+ len = recv_iov[1].iov_len;
+ if (recv_iov[1].iov_len >= 2) {
+ len = CVAL(body, 0x00);
+ if (len != recv_iov[1].iov_len) {
+ len = recv_iov[1].iov_len;
+ }
+ }
+
+ if (len == 24) {
+ lease = false;
+ } else if (len == 44) {
+ lease = true;
+ } else {
+ DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n",
+ (unsigned)len));
+ TALLOC_FREE(recv_iov);
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ smb2_transport_dead(transport, status);
+ return;
+ }
+
+ if (!lease && transport->oplock.handler) {
+ struct smb2_handle h;
+ uint8_t level;
+
+ level = CVAL(body, 0x02);
+ smb2_pull_handle(body+0x08, &h);
+
+ TALLOC_FREE(recv_iov);
+
+ transport->oplock.handler(transport, &h, level,
+ transport->oplock.private_data);
+ } else if (lease && transport->lease.handler) {
+ struct smb2_lease_break lb;
+
+ ZERO_STRUCT(lb);
+ lb.new_epoch = SVAL(body, 0x2);
+ lb.break_flags = SVAL(body, 0x4);
+ memcpy(&lb.current_lease.lease_key, body+0x8,
+ sizeof(struct smb2_lease_key));
+ lb.current_lease.lease_state = SVAL(body, 0x18);
+ lb.new_lease_state = SVAL(body, 0x1C);
+ lb.break_reason = SVAL(body, 0x20);
+ lb.access_mask_hint = SVAL(body, 0x24);
+ lb.share_mask_hint = SVAL(body, 0x28);
+
+ TALLOC_FREE(recv_iov);
+
+ transport->lease.handler(transport, &lb,
+ transport->lease.private_data);
+ } else {
+ DEBUG(5,("Got SMB2 %s break with no handler\n",
+ lease ? "lease" : "oplock"));
+ }
+ TALLOC_FREE(recv_iov);
+}
+
+NTSTATUS smb2_transport_compound_start(struct smb2_transport *transport,
+ uint32_t num)
+{
+ TALLOC_FREE(transport->compound.reqs);
+ ZERO_STRUCT(transport->compound);
+
+ transport->compound.reqs = talloc_zero_array(transport,
+ struct tevent_req *,
+ num);
+ if (transport->compound.reqs == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+void smb2_transport_compound_set_related(struct smb2_transport *transport,
+ bool related)
+{
+ transport->compound.related = related;
+}
+
+void smb2_transport_credits_ask_num(struct smb2_transport *transport,
+ uint16_t ask_num)
+{
+ smb2cli_conn_set_max_credits(transport->conn, ask_num);
+}
+
+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;
+
+ transport->idle.func(transport, transport->idle.private_data);
+
+ if (transport->idle.func == NULL) {
+ return;
+ }
+
+ if (!smbXcli_conn_is_connected(transport->conn)) {
+ return;
+ }
+
+ next = timeval_current_ofs_usec(transport->idle.period);
+ transport->idle.te = tevent_add_timer(transport->ev,
+ transport,
+ next,
+ idle_handler,
+ transport);
+}
+
+/*
+ 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)
+{
+ TALLOC_FREE(transport->idle.te);
+ ZERO_STRUCT(transport->idle);
+
+ if (idle_func == NULL) {
+ return;
+ }
+
+ if (!smbXcli_conn_is_connected(transport->conn)) {
+ return;
+ }
+
+ transport->idle.func = idle_func;
+ transport->idle.private_data = private_data;
+ transport->idle.period = period;
+
+ transport->idle.te = tevent_add_timer(transport->ev,
+ transport,
+ timeval_current_ofs_usec(period),
+ idle_handler,
+ transport);
+}
diff --git a/source4/libcli/smb2/util.c b/source4/libcli/smb2/util.c
new file mode 100644
index 0000000..f86a149
--- /dev/null
+++ b/source4/libcli/smb2/util.c
@@ -0,0 +1,363 @@
+/*
+ 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"
+#include "librpc/gen_ndr/ndr_security.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)
+{
+ struct smb2_create cr = {0};
+ struct smb2_handle h1 = {{0}};
+ union smb_setfileinfo setinfo;
+ NTSTATUS status;
+
+ cr = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_WRITE_ATTRIBUTE,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.create_disposition = FILE_OPEN,
+ .in.fname = name,
+ };
+ status = smb2_create(tree, tree, &cr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ h1 = cr.out.file.handle;
+
+ setinfo = (union smb_setfileinfo) {
+ .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
+ .basic_info.in.file.handle = h1,
+ .basic_info.in.attrib = attrib,
+ };
+
+ status = smb2_setinfo_file(tree, &setinfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_util_close(tree, h1);
+ return status;
+ }
+
+ smb2_util_close(tree, h1);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ get file attribute with SMB2
+*/
+NTSTATUS smb2_util_getatr(struct smb2_tree *tree, const char *fname,
+ uint16_t *attr, size_t *size, time_t *t)
+{
+ union smb_fileinfo parms;
+ NTSTATUS status;
+ struct smb2_create create_io = {0};
+
+ create_io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
+ create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ create_io.in.create_disposition = FILE_OPEN;
+ create_io.in.fname = fname;
+ status = smb2_create(tree, tree, &create_io);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ZERO_STRUCT(parms);
+ parms.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
+ parms.all_info2.in.file.handle = create_io.out.file.handle;
+ status = smb2_getinfo_file(tree, tree, &parms);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = smb2_util_close(tree, create_io.out.file.handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (size) {
+ *size = parms.all_info2.out.size;
+ }
+
+ if (t) {
+ *t = parms.all_info2.out.write_time;
+ }
+
+ if (attr) {
+ *attr = parms.all_info2.out.attrib;
+ }
+
+ return status;
+}
+
+
+/*
+ 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;
+ unsigned int count, i;
+ union smb_search_data *list;
+ TALLOC_CTX *tmp_ctx = talloc_new(tree);
+ struct smb2_find f;
+ struct smb2_create create_parm;
+ bool did_delete;
+
+ /* 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 */
+ 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;
+ }
+
+
+ do {
+ did_delete = false;
+
+ 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 */
+ 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++;
+ did_delete = true;
+ }
+ }
+ } while (did_delete);
+
+ 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 */
+ 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;
+}
+
+/*
+ check if two SMB2 file handles are the same
+*/
+bool smb2_util_handle_equal(const struct smb2_handle h1,
+ const struct smb2_handle h2)
+{
+ return (h1.data[0] == h2.data[0]) && (h1.data[1] == h2.data[1]);
+}
+
+bool smb2_util_handle_empty(const struct smb2_handle h)
+{
+ struct smb2_handle empty;
+
+ ZERO_STRUCT(empty);
+
+ return smb2_util_handle_equal(h, empty);
+}
+
+/****************************************************************************
+send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call
+****************************************************************************/
+NTSTATUS smb2_qpathinfo_alt_name(TALLOC_CTX *ctx, struct smb2_tree *tree,
+ const char *fname, const char **alt_name)
+{
+ union smb_fileinfo parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ struct smb2_create create_io = {0};
+
+ mem_ctx = talloc_new(ctx);
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ create_io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
+ create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ create_io.in.create_disposition = FILE_OPEN;
+ create_io.in.fname = fname;
+ status = smb2_create(tree, mem_ctx, &create_io);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ parms.alt_name_info.level = RAW_FILEINFO_SMB2_ALT_NAME_INFORMATION;
+ parms.alt_name_info.in.file.handle = create_io.out.file.handle;
+
+ status = smb2_getinfo_file(tree, mem_ctx, &parms);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ status = smb2_util_close(tree, create_io.out.file.handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ if (!parms.alt_name_info.out.fname.s) {
+ *alt_name = talloc_strdup(ctx, "");
+ } else {
+ *alt_name = talloc_strdup(ctx,
+ parms.alt_name_info.out.fname.s);
+ }
+
+ talloc_free(mem_ctx);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb2/write.c b/source4/libcli/smb2/write.c
new file mode 100644
index 0000000..62ffe2e
--- /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_ok(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/smb2/wscript_build b/source4/libcli/smb2/wscript_build
new file mode 100644
index 0000000..51ac0f2
--- /dev/null
+++ b/source4/libcli/smb2/wscript_build
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_SMB2',
+ source='transport.c request.c session.c tcon.c create.c close.c connect.c getinfo.c write.c read.c setinfo.c find.c ioctl.c logoff.c tdis.c flush.c lock.c notify.c cancel.c keepalive.c break.c util.c signing.c lease_break.c',
+ autoproto='smb2_proto.h',
+ deps='tevent-util cli_smb_common GNUTLS_HELPERS',
+ public_deps='smbclient-raw gensec samba-credentials tevent',
+ private_headers='smb2.h',
+ )
+
diff --git a/source4/libcli/smb_composite/appendacl.c b/source4/libcli/smb_composite/appendacl.c
new file mode 100644
index 0000000..faaabe0
--- /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;
+ 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) {
+ uint32_t i;
+ 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->ev;
+
+ /* 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.fnum = 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 0000000..ad50ae0
--- /dev/null
+++ b/source4/libcli/smb_composite/connect.c
@@ -0,0 +1,528 @@
+/*
+ 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"
+#include "lib/util/util_net.h"
+#include "libcli/smb/smbXcli_base.h"
+
+/* the stages of this call */
+enum connect_stage {CONNECT_SOCKET,
+ 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;
+ struct tevent_req *subreq;
+ struct nbt_name calling, called;
+};
+
+
+static void request_handler(struct smbcli_request *);
+static void composite_handler(struct composite_context *);
+static void subreq_handler(struct tevent_req *subreq);
+
+/*
+ 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);
+
+ if (state->io_tcon->tconx.out.options & SMB_EXTENDED_SIGNATURES) {
+ smb1cli_session_protect_session_key(io->out.tree->session->smbXcli);
+ }
+
+ 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 = TCONX_FLAG_EXTENDED_RESPONSE;
+ 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;
+ 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 = TCONX_FLAG_EXTENDED_RESPONSE;
+ state->io_tcon->tconx.in.flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
+ 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;
+}
+
+static NTSTATUS connect_send_session(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+
+ /* 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;
+}
+
+/*
+ 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->subreq);
+ TALLOC_FREE(state->subreq);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return connect_send_session(c, io);
+}
+
+/*
+ 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);
+
+ /* the socket is up - we can initialise the smbcli transport layer */
+ state->transport = smbcli_transport_init(state->sock, state, true,
+ &io->in.options);
+ NT_STATUS_HAVE_NO_MEMORY(state->transport);
+
+ state->subreq = smb_raw_negotiate_send(state,
+ state->transport->ev,
+ state->transport,
+ state->transport->options.min_protocol,
+ state->transport->options.max_protocol);
+ NT_STATUS_HAVE_NO_MEMORY(state->subreq);
+ tevent_req_set_callback(state->subreq, subreq_handler, c);
+ state->stage = CONNECT_NEGPROT;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ 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;
+
+ status = smbcli_sock_connect_recv(state->creq, state, &state->sock);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ 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);
+ }
+
+ /* next step is a negprot */
+ return connect_send_negprot(c, io);
+}
+
+
+/*
+ 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_SOCKET:
+ c->status = connect_socket(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;
+ case CONNECT_DONE:
+ 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);
+}
+
+/*
+ handler for completion of a tevent_req sub-request
+*/
+static void subreq_handler(struct tevent_req *subreq)
+{
+ struct composite_context *c =
+ tevent_req_callback_data(subreq,
+ 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;
+
+ c = talloc_zero(mem_ctx, struct composite_context);
+ if (c == NULL) {
+ goto nomem;
+ }
+
+ state = talloc_zero(c, struct connect_state);
+ if (state == NULL) {
+ goto nomem;
+ }
+
+ c->event_ctx = event_ctx;
+ if (c->event_ctx == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+
+ if (io->in.gensec_settings == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
+ return c;
+ }
+ state->io = io;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->private_data = state;
+
+ make_nbt_name_client(&state->calling,
+ cli_credentials_get_workstation(io->in.credentials));
+
+ nbt_choose_called_name(state, &state->called,
+ io->in.called_name, NBT_NAME_SERVER);
+
+ if (io->in.existing_conn != NULL) {
+ NTSTATUS status;
+
+ status = smbcli_transport_raw_init(state,
+ c->event_ctx,
+ &io->in.existing_conn,
+ &io->in.options,
+ &state->transport);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(c, status);
+ return c;
+ }
+
+ status = connect_send_session(c, io);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(c, status);
+ return c;
+ }
+
+ return c;
+ }
+
+ state->creq = smbcli_sock_connect_send(state,
+ NULL,
+ io->in.dest_ports,
+ io->in.dest_host,
+ resolve_ctx, c->event_ctx,
+ io->in.socket_options,
+ &state->calling,
+ &state->called);
+ if (state->creq == NULL) {
+ composite_error(c, NT_STATUS_NO_MEMORY);
+ return c;
+ }
+
+ state->stage = CONNECT_SOCKET;
+ state->creq->async.private_data = c;
+ state->creq->async.fn = composite_handler;
+
+ return c;
+nomem:
+ 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);
+ if (c == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return smb_composite_connect_recv(c, mem_ctx);
+}
diff --git a/source4/libcli/smb_composite/connect_nego.c b/source4/libcli/smb_composite/connect_nego.c
new file mode 100644
index 0000000..7224dfa
--- /dev/null
+++ b/source4/libcli/smb_composite/connect_nego.c
@@ -0,0 +1,211 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "libcli/composite/composite.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "libcli/smb/smbXcli_base.h"
+
+struct smb_connect_nego_state {
+ struct tevent_context *ev;
+ struct resolve_context *resolve_ctx;
+ const char *socket_options;
+ struct smbcli_options options;
+ const char *dest_hostname;
+ const char *dest_address;
+ const char **dest_ports;
+ const char *target_hostname;
+ struct nbt_name calling, called;
+ struct smbXcli_conn *conn;
+};
+
+static void smb_connect_nego_connect_done(struct composite_context *creq);
+static void smb_connect_nego_nego_done(struct tevent_req *subreq);
+
+struct tevent_req *smb_connect_nego_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resolve_context *resolve_ctx,
+ const struct smbcli_options *options,
+ const char *socket_options,
+ const char *dest_hostname,
+ const char *dest_address, /* optional */
+ const char **dest_ports,
+ const char *target_hostname,
+ const char *called_name,
+ const char *calling_name)
+{
+ struct tevent_req *req = NULL;
+ struct smb_connect_nego_state *state = NULL;
+ struct composite_context *creq = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct smb_connect_nego_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->resolve_ctx= resolve_ctx;
+ state->options = *options;
+ state->socket_options = socket_options;
+ state->dest_hostname = dest_hostname;
+ state->dest_address = dest_address;
+ state->dest_ports = dest_ports;
+ state->target_hostname = target_hostname;
+
+ make_nbt_name_client(&state->calling, calling_name);
+
+ nbt_choose_called_name(state, &state->called,
+ called_name, NBT_NAME_SERVER);
+ if (tevent_req_nomem(state->called.name, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ creq = smbcli_sock_connect_send(state,
+ state->dest_address,
+ state->dest_ports,
+ state->dest_hostname,
+ state->resolve_ctx,
+ state->ev,
+ state->socket_options,
+ &state->calling,
+ &state->called);
+ if (tevent_req_nomem(creq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ creq->async.private_data = req;
+ creq->async.fn = smb_connect_nego_connect_done;
+
+ return req;
+}
+
+static void smb_connect_nego_connect_done(struct composite_context *creq)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(creq->async.private_data,
+ struct tevent_req);
+ struct smb_connect_nego_state *state =
+ tevent_req_data(req,
+ struct smb_connect_nego_state);
+ struct tevent_req *subreq = NULL;
+ struct smbcli_socket *sock = NULL;
+ uint32_t smb1_capabilities;
+ uint32_t timeout_msec = state->options.request_timeout * 1000;
+ NTSTATUS status;
+
+ status = smbcli_sock_connect_recv(creq, state, &sock);
+ creq = NULL;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ TALLOC_FREE(sock->event.fde);
+ TALLOC_FREE(sock->event.te);
+
+ smb1_capabilities = 0;
+ smb1_capabilities |= CAP_LARGE_FILES;
+ smb1_capabilities |= CAP_NT_SMBS | CAP_RPC_REMOTE_APIS;
+ smb1_capabilities |= CAP_LOCK_AND_READ | CAP_NT_FIND;
+ smb1_capabilities |= CAP_DFS | CAP_W2K_SMBS;
+ smb1_capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX;
+ smb1_capabilities |= CAP_LWIO;
+
+ if (state->options.ntstatus_support) {
+ smb1_capabilities |= CAP_STATUS32;
+ }
+
+ if (state->options.unicode) {
+ smb1_capabilities |= CAP_UNICODE;
+ }
+
+ if (state->options.use_spnego) {
+ smb1_capabilities |= CAP_EXTENDED_SECURITY;
+ }
+
+ if (state->options.use_level2_oplocks) {
+ smb1_capabilities |= CAP_LEVEL_II_OPLOCKS;
+ }
+
+ state->conn = smbXcli_conn_create(state,
+ sock->sock->fd,
+ state->target_hostname,
+ state->options.signing,
+ smb1_capabilities,
+ &state->options.client_guid,
+ state->options.smb2_capabilities,
+ &state->options.smb3_capabilities);
+ if (tevent_req_nomem(state->conn, req)) {
+ return;
+ }
+ sock->sock->fd = -1;
+ TALLOC_FREE(sock);
+
+ subreq = smbXcli_negprot_send(state,
+ state->ev,
+ state->conn,
+ timeout_msec,
+ state->options.min_protocol,
+ state->options.max_protocol,
+ state->options.max_credits,
+ NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, smb_connect_nego_nego_done, req);
+}
+
+static void smb_connect_nego_nego_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ NTSTATUS status;
+
+ status = smbXcli_negprot_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS smb_connect_nego_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct smbXcli_conn **_conn)
+{
+ struct smb_connect_nego_state *state =
+ tevent_req_data(req,
+ struct smb_connect_nego_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *_conn = talloc_move(mem_ctx, &state->conn);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb_composite/fetchfile.c b/source4/libcli/smb_composite/fetchfile.c
new file mode 100644
index 0000000..30e3a62
--- /dev/null
+++ b/source4/libcli/smb_composite/fetchfile.c
@@ -0,0 +1,194 @@
+/*
+ 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;
+
+ 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;
+ default:
+ status = NT_STATUS_UNSUCCESSFUL;
+ 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_zero(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.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 0000000..242e2cc
--- /dev/null
+++ b/source4/libcli/smb_composite/fsinfo.c
@@ -0,0 +1,214 @@
+/*
+ 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 tevent_context *event_ctx)
+{
+ struct composite_context *c;
+ struct fsinfo_state *state;
+
+ c = talloc_zero(tree, struct composite_context);
+ if (c == NULL) goto failed;
+
+ c->event_ctx = event_ctx;
+ if (c->event_ctx == NULL) goto failed;
+
+ state = talloc(c, struct fsinfo_state);
+ if (state == NULL) goto failed;
+
+ state->io = io;
+
+ state->connect = talloc_zero(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.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 tevent_context *ev)
+{
+ struct composite_context *c = smb_composite_fsinfo_send(tree, io, resolve_ctx, ev);
+ 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 0000000..00456f1
--- /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->ev;
+
+ /* 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 0000000..2f00443
--- /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->ev;
+
+ 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 0000000..391ee08
--- /dev/null
+++ b/source4/libcli/smb_composite/sesssetup.c
@@ -0,0 +1,867 @@
+/*
+ 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 <tevent.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/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"
+#include "libcli/smb/smbXcli_base.h"
+
+struct sesssetup_state {
+ struct smbcli_session *session;
+ union smb_sesssetup setup;
+ const char *chosen_oid;
+ NTSTATUS remote_status;
+ NTSTATUS gensec_status;
+ struct smb_composite_sesssetup *io;
+ struct smbcli_request *req;
+ struct smbcli_request *check_req;
+ unsigned int logon_retries;
+};
+
+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_restart(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io);
+static NTSTATUS session_setup_spnego(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req);
+static void smb_composite_sesssetup_spnego_done1(struct tevent_req *subreq);
+static void smb_composite_sesssetup_spnego_done2(struct tevent_req *subreq);
+
+
+/*
+ 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 null_data_blob = data_blob(NULL, 0);
+ NTSTATUS session_key_err, nt_status;
+ struct smbcli_request *check_req = NULL;
+ const char *os = NULL;
+ const char *lanman = 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;
+ }
+ }
+ }
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+ os = state->setup.old.out.os;
+ lanman = state->setup.old.out.lanman;
+ 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 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_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;
+ }
+ }
+ }
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+ os = state->setup.nt1.out.os;
+ lanman = state->setup.nt1.out.lanman;
+ break;
+
+ case RAW_SESSSETUP_SPNEGO:
+ state->io->out.vuid = state->setup.spnego.out.vuid;
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
+ const char *principal;
+
+ /* we need to reset the vuid for a new try */
+ session->vuid = 0;
+
+ principal = gensec_get_target_principal(session->gensec);
+ if (principal == NULL) {
+ const char *hostname = gensec_get_target_hostname(session->gensec);
+ const char *service = gensec_get_target_service(session->gensec);
+ if (hostname != NULL && service != NULL) {
+ principal = talloc_asprintf(state, "%s/%s", service, hostname);
+ }
+ }
+ if (cli_credentials_failed_kerberos_login(state->io->in.credentials, principal, &state->logon_retries) ||
+ cli_credentials_wrong_password(state->io->in.credentials)) {
+ struct tevent_req *subreq = NULL;
+
+ nt_status = session_setup_spnego_restart(c, session, state->io);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("session_setup_spnego_restart() failed: %s\n",
+ nt_errstr(nt_status)));
+ c->status = nt_status;
+ composite_error(c, c->status);
+ return;
+ }
+
+ subreq = gensec_update_send(state, c->event_ctx,
+ session->gensec,
+ state->setup.spnego.out.secblob);
+ if (composite_nomem(subreq, c)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ smb_composite_sesssetup_spnego_done1,
+ c);
+ return;
+ }
+ }
+ if (GENSEC_UPDATE_IS_NTERROR(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+ if (NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ struct tevent_req *subreq = NULL;
+
+ /* 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 */
+
+ subreq = gensec_update_send(state, c->event_ctx,
+ session->gensec,
+ state->setup.spnego.out.secblob);
+ if (composite_nomem(subreq, c)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ smb_composite_sesssetup_spnego_done2,
+ c);
+ if (NT_STATUS_IS_OK(state->remote_status)) {
+ state->check_req = check_req;
+ } else {
+ TALLOC_FREE(check_req);
+ }
+ return;
+ } else {
+ state->setup.spnego.in.secblob = data_blob(NULL, 0);
+ }
+
+ if (cli_credentials_is_anonymous(state->io->in.credentials)) {
+ /*
+ * anonymous => no signing
+ */
+ } else if (NT_STATUS_IS_OK(state->remote_status)) {
+ DATA_BLOB session_key;
+
+ if (state->setup.spnego.in.secblob.length) {
+ c->status = NT_STATUS_INTERNAL_ERROR;
+ composite_error(c, c->status);
+ return;
+ }
+ session_key_err = gensec_session_key(session->gensec, session, &session_key);
+ if (NT_STATUS_IS_OK(session_key_err)) {
+ smb1cli_conn_activate_signing(session->transport->conn,
+ session_key,
+ null_data_blob);
+ }
+
+ c->status = smb1cli_session_set_session_key(session->smbXcli,
+ session_key);
+ data_blob_free(&session_key);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+ }
+
+ os = state->setup.spnego.out.os;
+ lanman = state->setup.spnego.out.lanman;
+ break;
+
+ case RAW_SESSSETUP_SMB2:
+ c->status = NT_STATUS_INTERNAL_ERROR;
+ composite_error(c, c->status);
+ return;
+ }
+
+ if (check_req) {
+ bool ok;
+
+ check_req->sign_caller_checks = false;
+
+ ok = smb1cli_conn_check_signing(check_req->transport->conn,
+ check_req->in.buffer, 1);
+ TALLOC_FREE(check_req);
+ if (!ok) {
+ c->status = NT_STATUS_ACCESS_DENIED;
+ composite_error(c, c->status);
+ return;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ if (os) {
+ session->os = talloc_strdup(session, os);
+ if (composite_nomem(session->os, c)) return;
+ } else {
+ session->os = NULL;
+ }
+ if (lanman) {
+ session->lanman = talloc_strdup(session, lanman);
+ if (composite_nomem(session->lanman, c)) return;
+ } else {
+ session->lanman = NULL;
+ }
+
+ 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);
+ const char *domain = cli_credentials_get_domain(io->in.credentials);
+
+ /*
+ * domain controllers tend to reject the NTLM v2 blob
+ * if the netbiosname is not valid (e.g. IP address or FQDN)
+ * so just leave it away (as Windows client do)
+ */
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(state, NULL, domain);
+
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ int flags = CLI_CRED_NTLM_AUTH;
+
+ 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) {
+ if (!cli_credentials_is_anonymous(io->in.credentials) &&
+ session->options.ntlmv2_auth &&
+ session->transport->options.use_spnego)
+ {
+ /*
+ * Don't send an NTLMv2_RESPONSE without NTLMSSP
+ * if we want to use spnego
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt_status = cli_credentials_get_ntlm_response(io->in.credentials, state,
+ &flags,
+ session->transport->negotiate.secblob,
+ NULL, /* server_timestamp */
+ 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)) {
+ /*
+ * plain text => no signing
+ */
+ return (*req)->status;
+ }
+
+ if (cli_credentials_is_anonymous(io->in.credentials)) {
+ /*
+ * anonymous => no signing
+ */
+ return (*req)->status;
+ }
+
+ smb1cli_conn_activate_signing(session->transport->conn,
+ session_key,
+ state->setup.nt1.in.password2);
+
+ nt_status = smb1cli_session_set_session_key(session->smbXcli,
+ session_key);
+ data_blob_free(&session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ 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);
+
+ /*
+ * domain controllers tend to reject the NTLM v2 blob
+ * if the netbiosname is not valid (e.g. IP address or FQDN)
+ * so just leave it away (as Windows client do)
+ */
+ DATA_BLOB session_key;
+
+ 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) {
+ DATA_BLOB names_blob = data_blob_null;
+ int flags = 0;
+
+ if (!cli_credentials_is_anonymous(io->in.credentials) &&
+ !session->options.lanman_auth)
+ {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ flags |= CLI_CRED_LANMAN_AUTH;
+
+ nt_status = cli_credentials_get_ntlm_response(io->in.credentials, state,
+ &flags,
+ session->transport->negotiate.secblob,
+ NULL, /* server_timestamp */
+ names_blob,
+ &state->setup.old.in.password,
+ NULL,
+ NULL, &session_key);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ nt_status = smb1cli_session_set_session_key(session->smbXcli,
+ session_key);
+ data_blob_free(&session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ } 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;
+}
+
+static NTSTATUS session_setup_spnego_restart(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io)
+{
+ struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
+ NTSTATUS status;
+
+ status = gensec_client_start(session, &session->gensec,
+ 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,
+ smbXcli_conn_remote_name(session->transport->conn));
+ 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;
+ }
+
+ state->setup.spnego.out.secblob =
+ session->transport->negotiate.secblob;
+ if (session->transport->negotiate.secblob.length) {
+ state->chosen_oid = GENSEC_OID_SPNEGO;
+ status = gensec_start_mech_by_oid(session->gensec,
+ state->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,
+ state->chosen_oid),
+ nt_errstr(status)));
+ state->setup.spnego.out.secblob = data_blob_null;
+ state->chosen_oid = GENSEC_OID_NTLMSSP;
+ status = gensec_start_mech_by_oid(session->gensec,
+ state->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,
+ state->chosen_oid),
+ nt_errstr(status)));
+ return status;
+ }
+ }
+ } else {
+ /* without a sec blob, means raw NTLMSSP */
+ state->chosen_oid = GENSEC_OID_NTLMSSP;
+ status = gensec_start_mech_by_oid(session->gensec,
+ state->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,
+ state->chosen_oid),
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ state->gensec_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+ state->remote_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+ return NT_STATUS_OK;
+}
+
+/*
+ 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);
+
+ 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;
+
+ *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
+ */
+ if (!smb1cli_conn_signing_is_active((*req)->transport->conn)) {
+ (*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;
+ enum smb_encryption_setting encryption_state =
+ cli_credentials_get_smb_encryption(io->in.credentials);
+ enum credentials_use_kerberos krb5_state =
+ cli_credentials_get_kerberos_state(io->in.credentials);
+
+ c = composite_create(session, session->transport->ev);
+ if (c == NULL) return NULL;
+
+ if (encryption_state > SMB_ENCRYPTION_DESIRED) {
+ composite_error(c, NT_STATUS_PROTOCOL_NOT_SUPPORTED);
+ return c;
+ }
+
+ state = talloc_zero(c, struct sesssetup_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->session = session;
+ 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) {
+ if (krb5_state == CRED_USE_KERBEROS_REQUIRED) {
+ composite_error(c, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT);
+ return c;
+ }
+ ZERO_STRUCT(io->out);
+ composite_done(c);
+ return c;
+ }
+
+ /* see what session setup interface we will use */
+ if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
+ if (krb5_state == CRED_USE_KERBEROS_REQUIRED) {
+ composite_error(c, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT);
+ return c;
+ }
+ status = session_setup_old(c, session, io, &state->req);
+ } else if (!session->transport->options.use_spnego ||
+ !(io->in.capabilities & CAP_EXTENDED_SECURITY)) {
+ if (krb5_state == CRED_USE_KERBEROS_REQUIRED) {
+ composite_error(c, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT);
+ return c;
+ }
+ status = session_setup_nt1(c, session, io, &state->req);
+ } else {
+ struct tevent_req *subreq = NULL;
+
+ status = session_setup_spnego_restart(c, session, io);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("session_setup_spnego_restart() failed: %s\n",
+ nt_errstr(status)));
+ c->status = status;
+ composite_error(c, c->status);
+ return c;
+ }
+
+ subreq = gensec_update_send(state, c->event_ctx,
+ session->gensec,
+ state->setup.spnego.out.secblob);
+ if (composite_nomem(subreq, c)) {
+ return c;
+ }
+ tevent_req_set_callback(subreq,
+ smb_composite_sesssetup_spnego_done1,
+ c);
+ return c;
+ }
+
+ 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;
+}
+
+static void smb_composite_sesssetup_spnego_done1(struct tevent_req *subreq)
+{
+ struct composite_context *c =
+ tevent_req_callback_data(subreq,
+ struct composite_context);
+ struct sesssetup_state *state =
+ talloc_get_type_abort(c->private_data,
+ struct sesssetup_state);
+ NTSTATUS status;
+
+ status = gensec_update_recv(subreq, state,
+ &state->setup.spnego.in.secblob);
+ TALLOC_FREE(subreq);
+ if (GENSEC_UPDATE_IS_NTERROR(status)) {
+ DEBUG(1, ("Failed initial gensec_update with mechanism %s: %s\n",
+ gensec_get_name_by_oid(state->session->gensec,
+ state->chosen_oid),
+ nt_errstr(status)));
+ c->status = status;
+ composite_error(c, c->status);
+ return;
+ }
+ state->gensec_status = status;
+
+ status = session_setup_spnego(c, state->session, state->io, &state->req);
+ if (!NT_STATUS_IS_OK(status)) {
+ c->status = status;
+ composite_error(c, c->status);
+ return;
+ }
+
+ composite_continue_smb(c, state->req, request_handler, c);
+}
+
+static void smb_composite_sesssetup_spnego_done2(struct tevent_req *subreq)
+{
+ struct composite_context *c =
+ tevent_req_callback_data(subreq,
+ struct composite_context);
+ struct sesssetup_state *state =
+ talloc_get_type_abort(c->private_data,
+ struct sesssetup_state);
+ struct smbcli_session *session = state->session;
+ NTSTATUS status;
+ const char *os = NULL;
+ const char *lanman = NULL;
+
+ status = gensec_update_recv(subreq, state,
+ &state->setup.spnego.in.secblob);
+ TALLOC_FREE(subreq);
+ if (GENSEC_UPDATE_IS_NTERROR(status)) {
+ DEBUG(1, ("Failed initial gensec_update with mechanism %s: %s\n",
+ gensec_get_name_by_oid(state->session->gensec,
+ state->chosen_oid),
+ nt_errstr(status)));
+ c->status = status;
+ composite_error(c, c->status);
+ return;
+ }
+ state->gensec_status = status;
+
+ if (NT_STATUS_IS_OK(state->remote_status)) {
+ if (state->setup.spnego.in.secblob.length) {
+ c->status = NT_STATUS_INTERNAL_ERROR;
+ composite_error(c, c->status);
+ return;
+ }
+ }
+
+ 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 &&
+ !smb1cli_conn_signing_is_active(state->req->transport->conn)) {
+ state->req->sign_caller_checks = true;
+ }
+ composite_continue_smb(c, state->req, request_handler, c);
+ return;
+ }
+
+ if (cli_credentials_is_anonymous(state->io->in.credentials)) {
+ /*
+ * anonymous => no signing
+ */
+ } else if (NT_STATUS_IS_OK(state->remote_status)) {
+ NTSTATUS session_key_err;
+ DATA_BLOB session_key;
+
+ session_key_err = gensec_session_key(session->gensec, session, &session_key);
+ if (NT_STATUS_IS_OK(session_key_err)) {
+ smb1cli_conn_activate_signing(session->transport->conn,
+ session_key,
+ data_blob_null);
+ }
+
+ c->status = smb1cli_session_set_session_key(session->smbXcli,
+ session_key);
+ data_blob_free(&session_key);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+ }
+
+ os = state->setup.spnego.out.os;
+ lanman = state->setup.spnego.out.lanman;
+
+ if (state->check_req) {
+ struct smbcli_request *check_req = state->check_req;
+ bool ok;
+
+ check_req->sign_caller_checks = false;
+
+ ok = smb1cli_conn_check_signing(check_req->transport->conn,
+ check_req->in.buffer, 1);
+ TALLOC_FREE(check_req);
+ if (!ok) {
+ c->status = NT_STATUS_ACCESS_DENIED;
+ composite_error(c, c->status);
+ return;
+ }
+ }
+
+ if (os) {
+ session->os = talloc_strdup(session, os);
+ if (composite_nomem(session->os, c)) return;
+ } else {
+ session->os = NULL;
+ }
+ if (lanman) {
+ session->lanman = talloc_strdup(session, lanman);
+ if (composite_nomem(session->lanman, c)) return;
+ } else {
+ session->lanman = NULL;
+ }
+
+ composite_done(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 0000000..0fa51b2
--- /dev/null
+++ b/source4/libcli/smb_composite/smb2.c
@@ -0,0 +1,447 @@
+/*
+ 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 <tevent.h>
+#include "lib/util/tevent_ntstatus.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->ev);
+ 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->ev);
+ 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->ev);
+ 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);
+}
+
+struct smb2_composite_setpathinfo_state {
+ struct smb2_tree *tree;
+ union smb_setfileinfo io;
+ NTSTATUS set_status;
+ struct smb2_create cr;
+ struct smb2_close cl;
+};
+
+static void smb2_composite_setpathinfo_create_done(struct smb2_request *smb2req);
+
+/*
+ composite SMB2 setpathinfo call
+*/
+struct tevent_req *smb2_composite_setpathinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smb2_tree *tree,
+ const union smb_setfileinfo *io)
+{
+ struct tevent_req *req;
+ struct smb2_composite_setpathinfo_state *state;
+ struct smb2_request *smb2req;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct smb2_composite_setpathinfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->tree = tree;
+ state->io = *io;
+
+ state->cr.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ state->cr.in.create_disposition = NTCREATEX_DISP_OPEN;
+ state->cr.in.share_access =
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ state->cr.in.create_options = 0;
+ state->cr.in.fname = state->io.generic.in.file.path;
+ if (state->cr.in.fname[0] == '\\') {
+ state->cr.in.fname++;
+ }
+
+ smb2req = smb2_create_send(tree, &state->cr);
+ if (tevent_req_nomem(smb2req, req)) {
+ return tevent_req_post(req, ev);
+ }
+ smb2req->async.fn = smb2_composite_setpathinfo_create_done;
+ smb2req->async.private_data = req;
+
+ return req;
+}
+
+static void smb2_composite_setpathinfo_setinfo_done(struct smb2_request *smb2req);
+
+static void smb2_composite_setpathinfo_create_done(struct smb2_request *smb2req)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(smb2req->async.private_data,
+ struct tevent_req);
+ struct smb2_composite_setpathinfo_state *state =
+ tevent_req_data(req,
+ struct smb2_composite_setpathinfo_state);
+ NTSTATUS status;
+
+ status = smb2_create_recv(smb2req, state, &state->cr);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->io.generic.in.file.handle = state->cr.out.file.handle;
+
+ smb2req = smb2_setinfo_file_send(state->tree, &state->io);
+ if (tevent_req_nomem(smb2req, req)) {
+ return;
+ }
+ smb2req->async.fn = smb2_composite_setpathinfo_setinfo_done;
+ smb2req->async.private_data = req;
+}
+
+static void smb2_composite_setpathinfo_close_done(struct smb2_request *smb2req);
+
+static void smb2_composite_setpathinfo_setinfo_done(struct smb2_request *smb2req)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(smb2req->async.private_data,
+ struct tevent_req);
+ struct smb2_composite_setpathinfo_state *state =
+ tevent_req_data(req,
+ struct smb2_composite_setpathinfo_state);
+ NTSTATUS status;
+
+ status = smb2_setinfo_recv(smb2req);
+ state->set_status = status;
+
+ state->cl.in.file.handle = state->io.generic.in.file.handle;
+
+ smb2req = smb2_close_send(state->tree, &state->cl);
+ if (tevent_req_nomem(smb2req, req)) {
+ return;
+ }
+ smb2req->async.fn = smb2_composite_setpathinfo_close_done;
+ smb2req->async.private_data = req;
+}
+
+static void smb2_composite_setpathinfo_close_done(struct smb2_request *smb2req)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(smb2req->async.private_data,
+ struct tevent_req);
+ struct smb2_composite_setpathinfo_state *state =
+ tevent_req_data(req,
+ struct smb2_composite_setpathinfo_state);
+ NTSTATUS status;
+
+ status = smb2_close_recv(smb2req, &state->cl);
+
+ if (tevent_req_nterror(req, state->set_status)) {
+ return;
+ }
+
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS smb2_composite_setpathinfo_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*
+ composite setpathinfo call
+ */
+NTSTATUS smb2_composite_setpathinfo(struct smb2_tree *tree, union smb_setfileinfo *io)
+{
+ struct tevent_req *subreq;
+ NTSTATUS status;
+ bool ok;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = tree->session->transport->ev;
+
+ if (frame == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ subreq = smb2_composite_setpathinfo_send(frame, ev, tree, io);
+ if (subreq == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ status = map_nt_error_from_unix_common(errno);
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = smb2_composite_setpathinfo_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb_composite/smb_composite.h b/source4/libcli/smb_composite/smb_composite.h
new file mode 100644
index 0000000..e43cbb9
--- /dev/null
+++ b/source4/libcli/smb_composite/smb_composite.h
@@ -0,0 +1,283 @@
+/*
+ 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
+*/
+
+#ifndef __SMB_COMPOSITE_H__
+#define __SMB_COMPOSITE_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 composite_context *smb_composite_loadfile_send(struct smbcli_tree *tree,
+ struct smb_composite_loadfile *io);
+NTSTATUS smb_composite_loadfile_recv(struct composite_context *c, TALLOC_CTX *mem_ctx);
+NTSTATUS smb_composite_loadfile(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_composite_loadfile *io);
+
+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 gensec_settings *gensec_settings;
+ } in;
+ struct {
+ uint8_t *data;
+ uint32_t size;
+ } out;
+};
+
+struct composite_context *smb_composite_fetchfile_send(struct smb_composite_fetchfile *io,
+ struct tevent_context *event_ctx);
+NTSTATUS smb_composite_fetchfile_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx);
+NTSTATUS smb_composite_fetchfile(struct smb_composite_fetchfile *io,
+ TALLOC_CTX *mem_ctx);
+
+/*
+ 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;
+};
+
+struct composite_context *smb_composite_savefile_send(struct smbcli_tree *tree,
+ struct smb_composite_savefile *io);
+NTSTATUS smb_composite_savefile_recv(struct composite_context *c);
+NTSTATUS smb_composite_savefile(struct smbcli_tree *tree,
+ struct smb_composite_savefile *io);
+
+/*
+ a composite request for a low level connection to a remote server. Includes
+
+ - socket establishment
+ - session request
+ - negprot
+*/
+struct tevent_req *smb_connect_nego_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resolve_context *resolve_ctx,
+ const struct smbcli_options *options,
+ const char *socket_options,
+ const char *dest_hostname,
+ const char *dest_address, /* optional */
+ const char **dest_ports,
+ const char *target_hostname,
+ const char *called_name,
+ const char *calling_name);
+NTSTATUS smb_connect_nego_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct smbXcli_conn **_conn);
+
+/*
+ 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 smbXcli_conn *existing_conn; /* optional */
+ struct cli_credentials *credentials;
+ bool fallback_to_anonymous;
+ const char *workgroup;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+ struct gensec_settings *gensec_settings;
+ } in;
+ struct {
+ struct smbcli_tree *tree;
+ bool anonymous_fallback_done;
+ } out;
+};
+
+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);
+NTSTATUS smb_composite_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx);
+NTSTATUS smb_composite_connect(struct smb_composite_connect *io, TALLOC_CTX *mem_ctx,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *ev);
+
+
+/*
+ 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;
+};
+
+struct composite_context *smb_composite_sesssetup_send(struct smbcli_session *session,
+ struct smb_composite_sesssetup *io);
+NTSTATUS smb_composite_sesssetup_recv(struct composite_context *c);
+NTSTATUS smb_composite_sesssetup(struct smbcli_session *session, struct smb_composite_sesssetup *io);
+
+/*
+ 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 gensec_settings *gensec_settings;
+ } in;
+
+ struct {
+ union smb_fsinfo *fsinfo;
+ } out;
+};
+
+struct composite_context *smb_composite_fsinfo_send(struct smbcli_tree *tree,
+ struct smb_composite_fsinfo *io,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx);
+NTSTATUS smb_composite_fsinfo_recv(struct composite_context *c, TALLOC_CTX *mem_ctx);
+NTSTATUS smb_composite_fsinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_composite_fsinfo *io,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *ev);
+
+/*
+ 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;
+};
+
+struct composite_context *smb_composite_appendacl_send(struct smbcli_tree *tree,
+ struct smb_composite_appendacl *io);
+NTSTATUS smb_composite_appendacl_recv(struct composite_context *c, TALLOC_CTX *mem_ctx);
+NTSTATUS smb_composite_appendacl(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_composite_appendacl *io);
+
+/*
+ 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 lpcfg_smb_ports() per
+ * destination or a list of explicit ports */
+ } in;
+ struct {
+ struct smbcli_socket *socket;
+ } out;
+};
+
+struct smbcli_session;
+struct resolve_context;
+
+struct composite_context *smb2_composite_unlink_send(struct smb2_tree *tree,
+ union smb_unlink *io);
+NTSTATUS smb2_composite_unlink(struct smb2_tree *tree, union smb_unlink *io);
+struct composite_context *smb2_composite_mkdir_send(struct smb2_tree *tree,
+ union smb_mkdir *io);
+NTSTATUS smb2_composite_mkdir(struct smb2_tree *tree, union smb_mkdir *io);
+struct composite_context *smb2_composite_rmdir_send(struct smb2_tree *tree,
+ struct smb_rmdir *io);
+NTSTATUS smb2_composite_rmdir(struct smb2_tree *tree, struct smb_rmdir *io);
+struct tevent_req *smb2_composite_setpathinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smb2_tree *tree,
+ const union smb_setfileinfo *io);
+NTSTATUS smb2_composite_setpathinfo_recv(struct tevent_req *req);
+NTSTATUS smb2_composite_setpathinfo(struct smb2_tree *tree, union smb_setfileinfo *io);
+
+#endif /* __SMB_COMPOSITE_H__ */
diff --git a/source4/libcli/util/clilsa.c b/source4/libcli/util/clilsa.c
new file mode 100644
index 0000000..8476fd8
--- /dev/null
+++ b/source4/libcli/util/clilsa.c
@@ -0,0 +1,548 @@
+/*
+ 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/smb2/smb2.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"
+#include "libcli/smb/smbXcli_base.h"
+
+struct smblsa_state {
+ struct dcerpc_binding_handle *binding_handle;
+ 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;
+ struct dcerpc_pipe *lsa_pipe;
+ 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 = TCONX_FLAG_EXTENDED_RESPONSE;
+ tcon.tconx.in.flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
+ 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;
+
+ if (tcon.tconx.out.options & SMB_EXTENDED_SIGNATURES) {
+ smb1cli_session_protect_session_key(cli->session->smbXcli);
+ }
+
+ lsa_pipe = dcerpc_pipe_init(lsa, cli->transport->ev);
+ 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;
+ }
+ lsa->binding_handle = lsa_pipe->binding_handle;
+
+ /* 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_r(lsa->binding_handle, lsa, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ talloc_free(lsa);
+ return r.out.result;
+ }
+
+ cli->lsa = lsa;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS smb2lsa_connect(struct smb2_tree *tree)
+{
+ struct smb2lsa_state *lsa = NULL;
+ struct dcerpc_pipe *lsa_pipe = NULL;
+ NTSTATUS status;
+ struct lsa_OpenPolicy2 r = {{0}, {0}};
+ const char *system_name = "\\";
+ struct lsa_ObjectAttribute attr = {0};
+ struct lsa_QosInfo qos = {0};
+
+ if (tree->lsa != NULL) {
+ return NT_STATUS_OK;
+ }
+
+ lsa = talloc(tree, struct smb2lsa_state);
+ if (lsa == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lsa_pipe = dcerpc_pipe_init(lsa, tree->session->transport->ev);
+ if (lsa_pipe == NULL) {
+ talloc_free(lsa);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* open the LSA pipe */
+ status = dcerpc_pipe_open_smb2(lsa_pipe, 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;
+ }
+ lsa->binding_handle = lsa_pipe->binding_handle;
+
+ /* 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_OpenPolicy2_r(lsa->binding_handle, lsa, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ talloc_free(lsa);
+ return r.out.result;
+ }
+
+ tree->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;
+
+ status = dcerpc_lsa_EnumAccountRights_r(cli->lsa->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return r.out.result;
+}
+
+
+NTSTATUS smb2lsa_sid_privileges(struct smb2_tree *tree, struct dom_sid *sid,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RightSet *rights)
+{
+ NTSTATUS status;
+ struct lsa_EnumAccountRights r = {{0}, {0}};
+
+ status = smb2lsa_connect(tree);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r.in.handle = &tree->lsa->handle;
+ r.in.sid = sid;
+ r.out.rights = rights;
+
+ status = dcerpc_lsa_EnumAccountRights_r(tree->lsa->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return r.out.result;
+}
+
+
+/*
+ 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;
+}
+
+
+NTSTATUS smb2lsa_sid_check_privilege(struct smb2_tree *tree,
+ const char *sid_str,
+ const char *privilege)
+{
+ struct lsa_RightSet rights = {0};
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct dom_sid *sid = NULL;
+ unsigned i;
+
+ mem_ctx = talloc_new(tree);
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sid = dom_sid_parse_talloc(mem_ctx, sid_str);
+ if (sid == NULL) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_INVALID_SID;
+ }
+
+ status = smb2lsa_sid_privileges(tree, 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_r(cli->lsa->binding_handle, mem_ctx2, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx2);
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ talloc_free(mem_ctx2);
+ return r.out.result;
+ }
+ if (names.count != 1) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (domains == NULL) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (domains->count != 1) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (names.names[0].sid_index != UINT32_MAX &&
+ names.names[0].sid_index >= domains->count)
+ {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ (*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);
+
+ 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_r(cli->lsa->binding_handle, mem_ctx2, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx2);
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ talloc_free(mem_ctx2);
+ return r.out.result;
+ }
+ if (sids.count != 1) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (domains->count != 1) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ sid_compose(&sid, domains->domains[0].sid, sids.sids[0].rid);
+
+ (*sid_str) = dom_sid_string(mem_ctx, &sid);
+
+ 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;
+
+ status = dcerpc_lsa_AddAccountRights_r(cli->lsa->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return r.out.result;
+}
+
+/*
+ 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;
+
+ status = dcerpc_lsa_RemoveAccountRights_r(cli->lsa->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return r.out.result;
+}
diff --git a/source4/libcli/util/pyerrors.h b/source4/libcli/util/pyerrors.h
new file mode 100644
index 0000000..1c91c5a
--- /dev/null
+++ b/source4/libcli/util/pyerrors.h
@@ -0,0 +1,79 @@
+/*
+ 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_FromHRESULT(err) Py_BuildValue("(i,s)", HRES_ERROR_V(err), discard_const_p(char, hresult_errstr_const(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_FromString(str) Py_BuildValue("(s)", discard_const_p(char, str))
+
+#define PyErr_SetWERROR(werr) \
+ PyErr_SetObject(PyObject_GetAttrString(PyImport_ImportModule("samba"),\
+ "WERRORError"), \
+ PyErr_FromWERROR(werr))
+
+#define PyErr_SetHRESULT(hresult) \
+ PyErr_SetObject(PyObject_GetAttrString(PyImport_ImportModule("samba"),\
+ "HRESULTError"), \
+ PyErr_FromHRESULT(hresult))
+
+#define PyErr_SetNTSTATUS(status) \
+ PyErr_SetObject(PyObject_GetAttrString(PyImport_ImportModule("samba"),\
+ "NTSTATUSError"), \
+ PyErr_FromNTSTATUS(status))
+
+#define PyErr_SetWERROR_and_string(werr, string) \
+ PyErr_SetObject(PyObject_GetAttrString(PyImport_ImportModule("samba"),\
+ "WERRORError"), \
+ Py_BuildValue("(i,s)", W_ERROR_V(werr), string))
+
+#define PyErr_SetHRESULT_and_string(hresult, string) \
+ PyErr_SetObject(PyObject_GetAttrString(PyImport_ImportModule("samba"),\
+ "HRESULTError"), \
+ Py_BuildValue("(i,s)", HRES_ERROR_V(hresult), string))
+
+#define PyErr_SetNTSTATUS_and_string(status, string) \
+ PyErr_SetObject(PyObject_GetAttrString(PyImport_ImportModule("samba"),\
+ "NTSTATUSError"), \
+ Py_BuildValue("(i,s)", NT_STATUS_V(status), string))
+
+#define PyErr_NTSTATUS_IS_ERR_RAISE(status) \
+ if (NT_STATUS_IS_ERR(status)) { \
+ PyErr_SetNTSTATUS(status); \
+ return NULL; \
+ }
+
+#define PyErr_NTSTATUS_NOT_OK_RAISE(status) \
+ if (!NT_STATUS_IS_OK(status)) { \
+ PyErr_SetNTSTATUS(status); \
+ return NULL; \
+ }
+
+#define PyErr_WERROR_NOT_OK_RAISE(status) \
+ if (!W_ERROR_IS_OK(status)) { \
+ PyErr_SetWERROR(status); \
+ return NULL; \
+ }
+
+#endif /* __PYERRORS_H__ */
diff --git a/source4/libcli/wbclient/wbclient.c b/source4/libcli/wbclient/wbclient.c
new file mode 100644
index 0000000..69d8b43
--- /dev/null
+++ b/source4/libcli/wbclient/wbclient.c
@@ -0,0 +1,193 @@
+/*
+ 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 <tevent.h>
+#include "nsswitch/winbind_client.h"
+#include "libcli/wbclient/wbclient.h"
+#include "libcli/security/dom_sid.h"
+#include "nsswitch/libwbclient/wbclient.h"
+
+NTSTATUS wbc_sids_to_xids(struct id_map *ids, uint32_t count)
+{
+ TALLOC_CTX *mem_ctx;
+ uint32_t i;
+ struct wbcDomainSid *sids;
+ struct wbcUnixId *xids;
+ wbcErr result;
+ bool wb_off;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sids = talloc_array(mem_ctx, struct wbcDomainSid, count);
+ if (sids == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ xids = talloc_array(mem_ctx, struct wbcUnixId, count);
+ if (xids == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<count; i++) {
+ memcpy(&sids[i], ids[i].sid, sizeof(struct dom_sid));
+ }
+
+ wb_off = winbind_env_set();
+ if (wb_off) {
+ (void)winbind_on();
+ }
+
+ result = wbcSidsToUnixIds(sids, count, xids);
+
+ if (wb_off) {
+ (void)winbind_off();
+ }
+
+ if (!WBC_ERROR_IS_OK(result)) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ for (i=0; i<count; i++) {
+ struct wbcUnixId *xid = &xids[i];
+ struct unixid *id = &ids[i].xid;
+
+ switch (xid->type) {
+ case WBC_ID_TYPE_UID:
+ id->type = ID_TYPE_UID;
+ id->id = xid->id.uid;
+ break;
+ case WBC_ID_TYPE_GID:
+ id->type = ID_TYPE_GID;
+ id->id = xid->id.gid;
+ break;
+ case WBC_ID_TYPE_BOTH:
+ id->type = ID_TYPE_BOTH;
+ id->id = xid->id.uid;
+ break;
+ case WBC_ID_TYPE_NOT_SPECIFIED:
+ id->type = ID_TYPE_NOT_SPECIFIED;
+ id->id = UINT32_MAX;
+ break;
+ }
+ ids[i].status = ID_MAPPED;
+ }
+
+ TALLOC_FREE(mem_ctx);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wbc_xids_to_sids(struct id_map *ids, uint32_t count)
+{
+ TALLOC_CTX *mem_ctx;
+ uint32_t i;
+ struct wbcDomainSid *sids;
+ struct wbcUnixId *xids;
+ wbcErr result;
+ bool wb_off;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sids = talloc_array(mem_ctx, struct wbcDomainSid, count);
+ if (sids == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ xids = talloc_array(mem_ctx, struct wbcUnixId, count);
+ if (xids == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<count; i++) {
+ struct id_map *id = &ids[i];
+ struct wbcUnixId *xid = &xids[i];
+
+ switch (id->xid.type) {
+ case ID_TYPE_UID:
+ *xid = (struct wbcUnixId) {
+ .type = WBC_ID_TYPE_UID,
+ .id.uid = id->xid.id
+ };
+ break;
+ case ID_TYPE_GID:
+ *xid = (struct wbcUnixId) {
+ .type = WBC_ID_TYPE_GID,
+ .id.uid = id->xid.id
+ };
+ break;
+ default:
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+ }
+ }
+
+ wb_off = winbind_env_set();
+ if (wb_off) {
+ (void)winbind_on();
+ }
+
+ result = wbcUnixIdsToSids(xids, count, sids);
+
+ if (wb_off) {
+ (void)winbind_off();
+ }
+
+ if (!WBC_ERROR_IS_OK(result)) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ for (i=0; i<count; i++) {
+ struct wbcDomainSid *sid = &sids[i];
+ struct wbcDomainSid null_sid = { 0 };
+ struct id_map *id = &ids[i];
+
+ if (memcmp(sid, &null_sid, sizeof(*sid)) != 0) {
+ struct dom_sid domsid;
+ id->status = ID_MAPPED;
+
+ memcpy(&domsid, sid, sizeof(struct dom_sid));
+ id->sid = dom_sid_dup(ids, &domsid);
+ if (id->sid == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ id->status = ID_UNMAPPED;
+ id->sid = NULL;
+ }
+ }
+
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/wbclient/wbclient.h b/source4/libcli/wbclient/wbclient.h
new file mode 100644
index 0000000..6334428
--- /dev/null
+++ b/source4/libcli/wbclient/wbclient.h
@@ -0,0 +1,25 @@
+/*
+ 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 "librpc/gen_ndr/idmap.h"
+
+NTSTATUS wbc_sids_to_xids(struct id_map *ids, uint32_t count);
+
+NTSTATUS wbc_xids_to_sids(struct id_map *ids, uint32_t count);
diff --git a/source4/libcli/wbclient/wscript_build b/source4/libcli/wbclient/wscript_build
new file mode 100644
index 0000000..679a281
--- /dev/null
+++ b/source4/libcli/wbclient/wscript_build
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+
+bld.SAMBA_LIBRARY('LIBWBCLIENT_OLD',
+ source='wbclient.c',
+ public_deps='samba-errors events wbclient',
+ cflags='-DWINBINDD_SOCKET_DIR=\"%s\"' % bld.env.WINBINDD_SOCKET_DIR,
+ deps='WB_REQTRANS NDR_WINBIND MESSAGING RPC_NDR_WINBIND',
+ private_library=True
+ )
+
diff --git a/source4/libcli/wrepl/winsrepl.c b/source4/libcli/wrepl/winsrepl.c
new file mode 100644
index 0000000..eab7430
--- /dev/null
+++ b/source4/libcli/wrepl/winsrepl.c
@@ -0,0 +1,1172 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ low level WINS replication client code
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher 2005-2010
+
+ 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/wrepl/winsrepl.h"
+#include "librpc/gen_ndr/ndr_winsrepl.h"
+#include "lib/stream/packet.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+
+/*
+ main context structure for the wins replication client library
+*/
+struct wrepl_socket {
+ struct {
+ struct tevent_context *ctx;
+ } event;
+
+ /* the default timeout for requests, 0 means no timeout */
+#define WREPL_SOCKET_REQUEST_TIMEOUT (60)
+ uint32_t request_timeout;
+
+ struct tevent_queue *request_queue;
+
+ struct tstream_context *stream;
+};
+
+bool wrepl_socket_is_connected(struct wrepl_socket *wrepl_sock)
+{
+ if (!wrepl_sock) {
+ return false;
+ }
+
+ if (!wrepl_sock->stream) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ 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 wrepl_socket *wrepl_socket;
+
+ wrepl_socket = talloc_zero(mem_ctx, struct wrepl_socket);
+ if (!wrepl_socket) {
+ return NULL;
+ }
+
+ wrepl_socket->event.ctx = event_ctx;
+ if (!wrepl_socket->event.ctx) {
+ goto failed;
+ }
+
+ wrepl_socket->request_queue = tevent_queue_create(wrepl_socket,
+ "wrepl request queue");
+ if (wrepl_socket->request_queue == NULL) {
+ goto failed;
+ }
+
+ wrepl_socket->request_timeout = WREPL_SOCKET_REQUEST_TIMEOUT;
+
+ return wrepl_socket;
+
+failed:
+ talloc_free(wrepl_socket);
+ return NULL;
+}
+
+/*
+ initialise a wrepl_socket from an already existing connection
+*/
+NTSTATUS wrepl_socket_donate_stream(struct wrepl_socket *wrepl_socket,
+ struct tstream_context **stream)
+{
+ if (wrepl_socket->stream) {
+ return NT_STATUS_CONNECTION_ACTIVE;
+ }
+
+ wrepl_socket->stream = talloc_move(wrepl_socket, stream);
+ return NT_STATUS_OK;
+}
+
+/*
+ initialise a wrepl_socket from an already existing connection
+*/
+NTSTATUS wrepl_socket_split_stream(struct wrepl_socket *wrepl_socket,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **stream)
+{
+ size_t num_requests;
+
+ if (!wrepl_socket->stream) {
+ return NT_STATUS_CONNECTION_INVALID;
+ }
+
+ num_requests = tevent_queue_length(wrepl_socket->request_queue);
+ if (num_requests > 0) {
+ return NT_STATUS_CONNECTION_IN_USE;
+ }
+
+ *stream = talloc_move(wrepl_socket, &wrepl_socket->stream);
+ return NT_STATUS_OK;
+}
+
+const char *wrepl_best_ip(struct loadparm_context *lp_ctx, const char *peer_ip)
+{
+ struct interface *ifaces;
+ load_interface_list(lp_ctx, lp_ctx, &ifaces);
+ return iface_list_best_ip(ifaces, peer_ip);
+}
+
+struct wrepl_connect_state {
+ struct {
+ struct wrepl_socket *wrepl_socket;
+ struct tevent_context *ev;
+ } caller;
+ struct tsocket_address *local_address;
+ struct tsocket_address *remote_address;
+ struct tstream_context *stream;
+};
+
+static void wrepl_connect_trigger(struct tevent_req *req,
+ void *private_date);
+
+struct tevent_req *wrepl_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wrepl_socket *wrepl_socket,
+ const char *our_ip, const char *peer_ip)
+{
+ struct tevent_req *req;
+ struct wrepl_connect_state *state;
+ int ret;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wrepl_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->caller.wrepl_socket = wrepl_socket;
+ state->caller.ev = ev;
+
+ if (wrepl_socket->stream) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_ACTIVE);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tsocket_address_inet_from_strings(state, "ipv4",
+ our_ip, 0,
+ &state->local_address);
+ if (ret != 0) {
+ NTSTATUS status = map_nt_error_from_unix_common(errno);
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tsocket_address_inet_from_strings(state, "ipv4",
+ peer_ip, WINS_REPLICATION_PORT,
+ &state->remote_address);
+ if (ret != 0) {
+ NTSTATUS status = map_nt_error_from_unix_common(errno);
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ ok = tevent_queue_add(wrepl_socket->request_queue,
+ ev,
+ req,
+ wrepl_connect_trigger,
+ NULL);
+ if (!ok) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+
+ if (wrepl_socket->request_timeout > 0) {
+ struct timeval endtime;
+ endtime = tevent_timeval_current_ofs(wrepl_socket->request_timeout, 0);
+ ok = tevent_req_set_endtime(req, ev, endtime);
+ if (!ok) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ return req;
+}
+
+static void wrepl_connect_done(struct tevent_req *subreq);
+
+static void wrepl_connect_trigger(struct tevent_req *req,
+ void *private_date)
+{
+ struct wrepl_connect_state *state = tevent_req_data(req,
+ struct wrepl_connect_state);
+ struct tevent_req *subreq;
+
+ subreq = tstream_inet_tcp_connect_send(state,
+ state->caller.ev,
+ state->local_address,
+ state->remote_address);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wrepl_connect_done, req);
+
+ return;
+}
+
+static void wrepl_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct wrepl_connect_state *state = tevent_req_data(req,
+ struct wrepl_connect_state);
+ int ret;
+ int sys_errno;
+
+ ret = tstream_inet_tcp_connect_recv(subreq, &sys_errno,
+ state, &state->stream, NULL);
+ if (ret != 0) {
+ NTSTATUS status = map_nt_error_from_unix_common(sys_errno);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+/*
+ connect a wrepl_socket to a WINS server - recv side
+*/
+NTSTATUS wrepl_connect_recv(struct tevent_req *req)
+{
+ struct wrepl_connect_state *state = tevent_req_data(req,
+ struct wrepl_connect_state);
+ struct wrepl_socket *wrepl_socket = state->caller.wrepl_socket;
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ wrepl_socket->stream = talloc_move(wrepl_socket, &state->stream);
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*
+ 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 tevent_req *subreq;
+ bool ok;
+ NTSTATUS status;
+
+ subreq = wrepl_connect_send(wrepl_socket, wrepl_socket->event.ctx,
+ wrepl_socket, our_ip, peer_ip);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+
+ ok = tevent_req_poll(subreq, wrepl_socket->event.ctx);
+ if (!ok) {
+ TALLOC_FREE(subreq);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = wrepl_connect_recv(subreq);
+ TALLOC_FREE(subreq);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+struct wrepl_request_state {
+ struct {
+ struct wrepl_socket *wrepl_socket;
+ struct tevent_context *ev;
+ } caller;
+ struct wrepl_send_ctrl ctrl;
+ struct {
+ struct wrepl_wrap wrap;
+ DATA_BLOB blob;
+ struct iovec iov;
+ } req;
+ bool one_way;
+ struct {
+ DATA_BLOB blob;
+ struct wrepl_packet *packet;
+ } rep;
+};
+
+static void wrepl_request_trigger(struct tevent_req *req,
+ void *private_data);
+
+struct tevent_req *wrepl_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wrepl_socket *wrepl_socket,
+ const struct wrepl_packet *packet,
+ const struct wrepl_send_ctrl *ctrl)
+{
+ struct tevent_req *req;
+ struct wrepl_request_state *state;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ bool ok;
+
+ if (wrepl_socket->event.ctx != ev) {
+ /* TODO: remove wrepl_socket->event.ctx !!! */
+ smb_panic("wrepl_associate_stop_send event context mismatch!");
+ return NULL;
+ }
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wrepl_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->caller.wrepl_socket = wrepl_socket;
+ state->caller.ev = ev;
+
+ if (ctrl) {
+ state->ctrl = *ctrl;
+ }
+
+ if (wrepl_socket->stream == NULL) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ state->req.wrap.packet = *packet;
+ ndr_err = ndr_push_struct_blob(&state->req.blob, state,
+ &state->req.wrap,
+ (ndr_push_flags_fn_t)ndr_push_wrepl_wrap);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ state->req.iov.iov_base = (char *) state->req.blob.data;
+ state->req.iov.iov_len = state->req.blob.length;
+
+ ok = tevent_queue_add(wrepl_socket->request_queue,
+ ev,
+ req,
+ wrepl_request_trigger,
+ NULL);
+ if (!ok) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+
+ if (wrepl_socket->request_timeout > 0) {
+ struct timeval endtime;
+ endtime = tevent_timeval_current_ofs(wrepl_socket->request_timeout, 0);
+ ok = tevent_req_set_endtime(req, ev, endtime);
+ if (!ok) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ return req;
+}
+
+static void wrepl_request_writev_done(struct tevent_req *subreq);
+
+static void wrepl_request_trigger(struct tevent_req *req,
+ void *private_data)
+{
+ struct wrepl_request_state *state = tevent_req_data(req,
+ struct wrepl_request_state);
+ struct tevent_req *subreq;
+
+ if (state->caller.wrepl_socket->stream == NULL) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return;
+ }
+
+ if (DEBUGLVL(10)) {
+ DEBUG(10,("Sending WINS packet of length %u\n",
+ (unsigned)state->req.blob.length));
+ NDR_PRINT_DEBUG(wrepl_packet, &state->req.wrap.packet);
+ }
+
+ subreq = tstream_writev_send(state,
+ state->caller.ev,
+ state->caller.wrepl_socket->stream,
+ &state->req.iov, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wrepl_request_writev_done, req);
+}
+
+static void wrepl_request_disconnect_done(struct tevent_req *subreq);
+static void wrepl_request_read_pdu_done(struct tevent_req *subreq);
+
+static void wrepl_request_writev_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct wrepl_request_state *state = tevent_req_data(req,
+ struct wrepl_request_state);
+ int ret;
+ int sys_errno;
+
+ ret = tstream_writev_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ NTSTATUS status = map_nt_error_from_unix_common(sys_errno);
+ TALLOC_FREE(state->caller.wrepl_socket->stream);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (state->caller.wrepl_socket->stream == NULL) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return;
+ }
+
+ if (state->ctrl.disconnect_after_send) {
+ subreq = tstream_disconnect_send(state,
+ state->caller.ev,
+ state->caller.wrepl_socket->stream);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wrepl_request_disconnect_done, req);
+ return;
+ }
+
+ if (state->ctrl.send_only) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = tstream_read_pdu_blob_send(state,
+ state->caller.ev,
+ state->caller.wrepl_socket->stream,
+ 4, /* initial_read_size */
+ packet_full_request_u32,
+ NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wrepl_request_read_pdu_done, req);
+}
+
+static void wrepl_request_disconnect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct wrepl_request_state *state = tevent_req_data(req,
+ struct wrepl_request_state);
+ int ret;
+ int sys_errno;
+
+ ret = tstream_disconnect_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ NTSTATUS status = map_nt_error_from_unix_common(sys_errno);
+ TALLOC_FREE(state->caller.wrepl_socket->stream);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ DEBUG(10,("WINS connection disconnected\n"));
+ TALLOC_FREE(state->caller.wrepl_socket->stream);
+
+ tevent_req_done(req);
+}
+
+static void wrepl_request_read_pdu_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct wrepl_request_state *state = tevent_req_data(req,
+ struct wrepl_request_state);
+ NTSTATUS status;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ status = tstream_read_pdu_blob_recv(subreq, state, &state->rep.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(state->caller.wrepl_socket->stream);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ state->rep.packet = talloc(state, struct wrepl_packet);
+ if (tevent_req_nomem(state->rep.packet, req)) {
+ return;
+ }
+
+ blob.data = state->rep.blob.data + 4;
+ blob.length = state->rep.blob.length - 4;
+
+ /* we have a full request - parse it */
+ ndr_err = ndr_pull_struct_blob(&blob,
+ state->rep.packet,
+ state->rep.packet,
+ (ndr_pull_flags_fn_t)ndr_pull_wrepl_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (DEBUGLVL(10)) {
+ DEBUG(10,("Received WINS packet of length %u\n",
+ (unsigned)state->rep.blob.length));
+ NDR_PRINT_DEBUG(wrepl_packet, state->rep.packet);
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS wrepl_request_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_packet **packet)
+{
+ struct wrepl_request_state *state = tevent_req_data(req,
+ struct wrepl_request_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ TALLOC_FREE(state->caller.wrepl_socket->stream);
+ tevent_req_received(req);
+ return status;
+ }
+
+ if (packet) {
+ *packet = talloc_move(mem_ctx, &state->rep.packet);
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*
+ a full WINS replication request/response
+*/
+NTSTATUS wrepl_request(struct wrepl_socket *wrepl_socket,
+ TALLOC_CTX *mem_ctx,
+ const struct wrepl_packet *req_packet,
+ struct wrepl_packet **reply_packet)
+{
+ struct tevent_req *subreq;
+ bool ok;
+ NTSTATUS status;
+
+ subreq = wrepl_request_send(mem_ctx, wrepl_socket->event.ctx,
+ wrepl_socket, req_packet, NULL);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+
+ ok = tevent_req_poll(subreq, wrepl_socket->event.ctx);
+ if (!ok) {
+ TALLOC_FREE(subreq);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = wrepl_request_recv(subreq, mem_ctx, reply_packet);
+ TALLOC_FREE(subreq);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+
+struct wrepl_associate_state {
+ struct wrepl_packet packet;
+ uint32_t assoc_ctx;
+ uint16_t major_version;
+};
+
+static void wrepl_associate_done(struct tevent_req *subreq);
+
+struct tevent_req *wrepl_associate_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wrepl_socket *wrepl_socket,
+ const struct wrepl_associate *io)
+{
+ struct tevent_req *req;
+ struct wrepl_associate_state *state;
+ struct tevent_req *subreq;
+
+ if (wrepl_socket->event.ctx != ev) {
+ /* TODO: remove wrepl_socket->event.ctx !!! */
+ smb_panic("wrepl_associate_send event context mismatch!");
+ return NULL;
+ }
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wrepl_associate_state);
+ if (req == NULL) {
+ return NULL;
+ };
+
+ state->packet.opcode = WREPL_OPCODE_BITS;
+ state->packet.mess_type = WREPL_START_ASSOCIATION;
+ state->packet.message.start.minor_version = 2;
+ state->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
+ */
+ state->packet.padding = data_blob_talloc(state, NULL, 21);
+ if (tevent_req_nomem(state->packet.padding.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+ memset(state->packet.padding.data, 0, state->packet.padding.length);
+
+ subreq = wrepl_request_send(state, ev, wrepl_socket, &state->packet, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wrepl_associate_done, req);
+
+ return req;
+}
+
+static void wrepl_associate_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct wrepl_associate_state *state = tevent_req_data(req,
+ struct wrepl_associate_state);
+ NTSTATUS status;
+ struct wrepl_packet *packet;
+
+ status = wrepl_request_recv(subreq, state, &packet);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (packet->mess_type != WREPL_START_ASSOCIATION_REPLY) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ state->assoc_ctx = packet->message.start_reply.assoc_ctx;
+ state->major_version = packet->message.start_reply.major_version;
+
+ tevent_req_done(req);
+}
+
+/*
+ setup an association - recv
+*/
+NTSTATUS wrepl_associate_recv(struct tevent_req *req,
+ struct wrepl_associate *io)
+{
+ struct wrepl_associate_state *state = tevent_req_data(req,
+ struct wrepl_associate_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ io->out.assoc_ctx = state->assoc_ctx;
+ io->out.major_version = state->major_version;
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*
+ setup an association - sync api
+*/
+NTSTATUS wrepl_associate(struct wrepl_socket *wrepl_socket,
+ struct wrepl_associate *io)
+{
+ struct tevent_req *subreq;
+ bool ok;
+ NTSTATUS status;
+
+ subreq = wrepl_associate_send(wrepl_socket, wrepl_socket->event.ctx,
+ wrepl_socket, io);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+
+ ok = tevent_req_poll(subreq, wrepl_socket->event.ctx);
+ if (!ok) {
+ TALLOC_FREE(subreq);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = wrepl_associate_recv(subreq, io);
+ TALLOC_FREE(subreq);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+struct wrepl_associate_stop_state {
+ struct wrepl_packet packet;
+ struct wrepl_send_ctrl ctrl;
+};
+
+static void wrepl_associate_stop_done(struct tevent_req *subreq);
+
+struct tevent_req *wrepl_associate_stop_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wrepl_socket *wrepl_socket,
+ const struct wrepl_associate_stop *io)
+{
+ struct tevent_req *req;
+ struct wrepl_associate_stop_state *state;
+ struct tevent_req *subreq;
+
+ if (wrepl_socket->event.ctx != ev) {
+ /* TODO: remove wrepl_socket->event.ctx !!! */
+ smb_panic("wrepl_associate_stop_send event context mismatch!");
+ return NULL;
+ }
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wrepl_associate_stop_state);
+ if (req == NULL) {
+ return NULL;
+ };
+
+ state->packet.opcode = WREPL_OPCODE_BITS;
+ state->packet.assoc_ctx = io->in.assoc_ctx;
+ state->packet.mess_type = WREPL_STOP_ASSOCIATION;
+ state->packet.message.stop.reason = io->in.reason;
+
+ if (io->in.reason == 0) {
+ state->ctrl.send_only = true;
+ state->ctrl.disconnect_after_send = true;
+ }
+
+ subreq = wrepl_request_send(state, ev, wrepl_socket, &state->packet, &state->ctrl);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wrepl_associate_stop_done, req);
+
+ return req;
+}
+
+static void wrepl_associate_stop_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct wrepl_associate_stop_state *state = tevent_req_data(req,
+ struct wrepl_associate_stop_state);
+ NTSTATUS status;
+
+ /* currently we don't care about a possible response */
+ status = wrepl_request_recv(subreq, state, NULL);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+/*
+ stop an association - recv
+*/
+NTSTATUS wrepl_associate_stop_recv(struct tevent_req *req,
+ struct wrepl_associate_stop *io)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*
+ setup an association - sync api
+*/
+NTSTATUS wrepl_associate_stop(struct wrepl_socket *wrepl_socket,
+ struct wrepl_associate_stop *io)
+{
+ struct tevent_req *subreq;
+ bool ok;
+ NTSTATUS status;
+
+ subreq = wrepl_associate_stop_send(wrepl_socket, wrepl_socket->event.ctx,
+ wrepl_socket, io);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+
+ ok = tevent_req_poll(subreq, wrepl_socket->event.ctx);
+ if (!ok) {
+ TALLOC_FREE(subreq);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = wrepl_associate_stop_recv(subreq, io);
+ TALLOC_FREE(subreq);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+struct wrepl_pull_table_state {
+ struct wrepl_packet packet;
+ uint32_t num_partners;
+ struct wrepl_wins_owner *partners;
+};
+
+static void wrepl_pull_table_done(struct tevent_req *subreq);
+
+struct tevent_req *wrepl_pull_table_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wrepl_socket *wrepl_socket,
+ const struct wrepl_pull_table *io)
+{
+ struct tevent_req *req;
+ struct wrepl_pull_table_state *state;
+ struct tevent_req *subreq;
+
+ if (wrepl_socket->event.ctx != ev) {
+ /* TODO: remove wrepl_socket->event.ctx !!! */
+ smb_panic("wrepl_pull_table_send event context mismatch!");
+ return NULL;
+ }
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wrepl_pull_table_state);
+ if (req == NULL) {
+ return NULL;
+ };
+
+ state->packet.opcode = WREPL_OPCODE_BITS;
+ state->packet.assoc_ctx = io->in.assoc_ctx;
+ state->packet.mess_type = WREPL_REPLICATION;
+ state->packet.message.replication.command = WREPL_REPL_TABLE_QUERY;
+
+ subreq = wrepl_request_send(state, ev, wrepl_socket, &state->packet, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wrepl_pull_table_done, req);
+
+ return req;
+}
+
+static void wrepl_pull_table_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct wrepl_pull_table_state *state = tevent_req_data(req,
+ struct wrepl_pull_table_state);
+ NTSTATUS status;
+ struct wrepl_packet *packet;
+ struct wrepl_table *table;
+
+ status = wrepl_request_recv(subreq, state, &packet);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (packet->mess_type != WREPL_REPLICATION) {
+ tevent_req_nterror(req, NT_STATUS_NETWORK_ACCESS_DENIED);
+ return;
+ }
+
+ if (packet->message.replication.command != WREPL_REPL_TABLE_REPLY) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ table = &packet->message.replication.info.table;
+
+ state->num_partners = table->partner_count;
+ state->partners = talloc_move(state, &table->partners);
+
+ tevent_req_done(req);
+}
+
+/*
+ fetch the partner tables - recv
+*/
+NTSTATUS wrepl_pull_table_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_pull_table *io)
+{
+ struct wrepl_pull_table_state *state = tevent_req_data(req,
+ struct wrepl_pull_table_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ io->out.num_partners = state->num_partners;
+ io->out.partners = talloc_move(mem_ctx, &state->partners);
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*
+ 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 tevent_req *subreq;
+ bool ok;
+ NTSTATUS status;
+
+ subreq = wrepl_pull_table_send(mem_ctx, wrepl_socket->event.ctx,
+ wrepl_socket, io);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+
+ ok = tevent_req_poll(subreq, wrepl_socket->event.ctx);
+ if (!ok) {
+ TALLOC_FREE(subreq);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = wrepl_pull_table_recv(subreq, mem_ctx, io);
+ TALLOC_FREE(subreq);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+
+struct wrepl_pull_names_state {
+ struct {
+ const struct wrepl_pull_names *io;
+ } caller;
+ struct wrepl_packet packet;
+ uint32_t num_names;
+ struct wrepl_name *names;
+};
+
+static void wrepl_pull_names_done(struct tevent_req *subreq);
+
+struct tevent_req *wrepl_pull_names_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct wrepl_socket *wrepl_socket,
+ const struct wrepl_pull_names *io)
+{
+ struct tevent_req *req;
+ struct wrepl_pull_names_state *state;
+ struct tevent_req *subreq;
+
+ if (wrepl_socket->event.ctx != ev) {
+ /* TODO: remove wrepl_socket->event.ctx !!! */
+ smb_panic("wrepl_pull_names_send event context mismatch!");
+ return NULL;
+ }
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wrepl_pull_names_state);
+ if (req == NULL) {
+ return NULL;
+ };
+ state->caller.io = io;
+
+ state->packet.opcode = WREPL_OPCODE_BITS;
+ state->packet.assoc_ctx = io->in.assoc_ctx;
+ state->packet.mess_type = WREPL_REPLICATION;
+ state->packet.message.replication.command = WREPL_REPL_SEND_REQUEST;
+ state->packet.message.replication.info.owner = io->in.partner;
+
+ subreq = wrepl_request_send(state, ev, wrepl_socket, &state->packet, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wrepl_pull_names_done, req);
+
+ return req;
+}
+
+static void wrepl_pull_names_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct wrepl_pull_names_state *state = tevent_req_data(req,
+ struct wrepl_pull_names_state);
+ NTSTATUS status;
+ struct wrepl_packet *packet;
+ uint32_t i;
+
+ status = wrepl_request_recv(subreq, state, &packet);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (packet->mess_type != WREPL_REPLICATION) {
+ tevent_req_nterror(req, NT_STATUS_NETWORK_ACCESS_DENIED);
+ return;
+ }
+
+ if (packet->message.replication.command != WREPL_REPL_SEND_REPLY) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ state->num_names = packet->message.replication.info.reply.num_names;
+
+ state->names = talloc_array(state, struct wrepl_name, state->num_names);
+ if (tevent_req_nomem(state->names, req)) {
+ return;
+ }
+
+ /* convert the list of names and addresses to a sane format */
+ for (i=0; i < state->num_names; i++) {
+ struct wrepl_wins_name *wname = &packet->message.replication.info.reply.names[i];
+ struct wrepl_name *name = &state->names[i];
+
+ name->name = *wname->name;
+ talloc_steal(state->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(state->names,
+ state->caller.io->in.partner.address);
+ if (tevent_req_nomem(name->owner, req)) {
+ return;
+ }
+
+ /* trying to save 1 or 2 bytes on the wire isn't a good idea */
+ if (wname->flags & 2) {
+ uint32_t j;
+
+ name->num_addresses = wname->addresses.addresses.num_ips;
+ name->addresses = talloc_array(state->names,
+ struct wrepl_address,
+ name->num_addresses);
+ if (tevent_req_nomem(name->addresses, req)) {
+ return;
+ }
+
+ for (j=0;j<name->num_addresses;j++) {
+ name->addresses[j].owner =
+ talloc_move(name->addresses,
+ &wname->addresses.addresses.ips[j].owner);
+ name->addresses[j].address =
+ talloc_move(name->addresses,
+ &wname->addresses.addresses.ips[j].ip);
+ }
+ } else {
+ name->num_addresses = 1;
+ name->addresses = talloc_array(state->names,
+ struct wrepl_address,
+ name->num_addresses);
+ if (tevent_req_nomem(name->addresses, req)) {
+ return;
+ }
+
+ name->addresses[0].owner = talloc_strdup(name->addresses, name->owner);
+ if (tevent_req_nomem(name->addresses[0].owner, req)) {
+ return;
+ }
+ name->addresses[0].address = talloc_move(name->addresses,
+ &wname->addresses.ip);
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+/*
+ fetch the names for a WINS partner - recv
+*/
+NTSTATUS wrepl_pull_names_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_pull_names *io)
+{
+ struct wrepl_pull_names_state *state = tevent_req_data(req,
+ struct wrepl_pull_names_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ io->out.num_names = state->num_names;
+ io->out.names = talloc_move(mem_ctx, &state->names);
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ 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 tevent_req *subreq;
+ bool ok;
+ NTSTATUS status;
+
+ subreq = wrepl_pull_names_send(mem_ctx, wrepl_socket->event.ctx,
+ wrepl_socket, io);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+
+ ok = tevent_req_poll(subreq, wrepl_socket->event.ctx);
+ if (!ok) {
+ TALLOC_FREE(subreq);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = wrepl_pull_names_recv(subreq, mem_ctx, io);
+ TALLOC_FREE(subreq);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/wrepl/winsrepl.h b/source4/libcli/wrepl/winsrepl.h
new file mode 100644
index 0000000..bc35544
--- /dev/null
+++ b/source4/libcli/wrepl/winsrepl.h
@@ -0,0 +1,110 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ structures for WINS replication client library
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher 2005-2010
+
+ 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"
+
+struct wrepl_request;
+struct wrepl_socket;
+
+struct wrepl_send_ctrl {
+ bool send_only;
+ bool disconnect_after_send;
+};
+
+/*
+ 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))
+
+struct wrepl_address {
+ const char *owner;
+ const char *address;
+};
+
+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 *addresses;
+};
+
+/*
+ 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 *names;
+ } out;
+};
+
+struct tstream_context;
+
+#include "libcli/wrepl/winsrepl_proto.h"
diff --git a/source4/libcli/wscript_build b/source4/libcli/wscript_build
new file mode 100644
index 0000000..f1bb6bd
--- /dev/null
+++ b/source4/libcli/wscript_build
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+
+bld.RECURSE('ldap')
+bld.RECURSE('wbclient')
+
+bld.SAMBA_SUBSYSTEM('LIBSAMBA_TSOCKET',
+ source='../../libcli/util/tstream.c',
+ public_deps='LIBTSOCKET tevent-util'
+ )
+
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_LSA',
+ source='util/clilsa.c',
+ autoproto='util/clilsa.h',
+ public_deps='RPC_NDR_LSA dcerpc',
+ deps='samba-security'
+ )
+
+
+bld.SAMBA_SUBSYSTEM('cli_composite',
+ source='composite/composite.c',
+ autoproto='composite/proto.h',
+ public_deps='events'
+ )
+
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_SMB_COMPOSITE',
+ source='''
+ smb_composite/loadfile.c
+ smb_composite/savefile.c
+ smb_composite/connect_nego.c
+ smb_composite/connect.c
+ smb_composite/sesssetup.c
+ smb_composite/fetchfile.c
+ smb_composite/appendacl.c
+ smb_composite/fsinfo.c
+ smb_composite/smb2.c
+ ''',
+ deps='LIBCLI_SMB2 tevent-util',
+ public_deps='cli_composite samba-credentials gensec LIBCLI_RESOLVE tevent',
+ private_headers='smb_composite/smb_composite.h',
+ )
+
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_DGRAM',
+ source='dgram/dgramsocket.c dgram/mailslot.c dgram/netlogon.c dgram/browse.c',
+ public_deps='cli-nbt ndr LIBCLI_RESOLVE LIBCLI_NETLOGON LIBCLI_RESOLVE'
+ )
+
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_WREPL',
+ source='wrepl/winsrepl.c',
+ autoproto='wrepl/winsrepl_proto.h',
+ public_deps='NDR_WINSREPL samba_socket events LIBPACKET LIBSAMBA_TSOCKET'
+ )
+
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_RESOLVE',
+ source='resolve/resolve.c',
+ autoproto='resolve/proto.h',
+ deps='roken',
+ public_deps='ndr_nbt LIBTSOCKET'
+ )
+
+
+bld.SAMBA_SUBSYSTEM('LP_RESOLVE',
+ source='resolve/bcast.c resolve/nbtlist.c resolve/wins.c resolve/dns_ex.c resolve/host.c resolve/lmhosts.c resolve/resolve_lp.c',
+ autoproto='resolve/lp_proto.h',
+ deps='cli-nbt samba-hostconfig netif addns'
+ )
+
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_FINDDCS',
+ source='finddcs_cldap.c',
+ autoproto='finddcs_proto.h',
+ public_deps='cli_cldap'
+ )
+
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_SMB',
+ source='clireadwrite.c cliconnect.c clifile.c clilist.c clitrans2.c climessage.c clideltree.c',
+ private_headers='libcli.h:smb_cli.h',
+ public_deps='smbclient-raw samba-errors LIBCLI_AUTH LIBCLI_SMB_COMPOSITE cli-nbt samba-security LIBCLI_RESOLVE LIBCLI_DGRAM LIBCLI_SMB2 LIBCLI_FINDDCS samba_socket'
+ )
+
+
+bld.SAMBA_LIBRARY('smbclient-raw',
+ source='raw/rawfile.c raw/smb_signing.c raw/clisocket.c raw/clitransport.c raw/clisession.c raw/clitree.c raw/clierror.c raw/rawrequest.c raw/rawreadwrite.c raw/rawsearch.c raw/rawsetfileinfo.c raw/raweas.c raw/rawtrans.c raw/clioplock.c raw/rawnegotiate.c raw/rawfsinfo.c raw/rawfileinfo.c raw/rawnotify.c raw/rawioctl.c raw/rawacl.c raw/rawdate.c raw/rawlpq.c raw/rawshadow.c',
+ autoproto='raw/raw_proto.h',
+ public_deps='samba_socket LIBPACKET LIBCRYPTO',
+ deps='cli_composite LIBCLI_RESOLVE samba-security ndr samba-util samba-errors charset talloc LIBCLI_SMB_COMPOSITE tevent cli_smb_common',
+ private_headers='raw/request.h:smb_request.h raw/signing.h:smb_raw_signing.h raw/libcliraw.h:smb_cliraw.h raw/interfaces.h:smb_raw_interfaces.h raw/smb.h:smb_raw.h raw/trans2.h:smb_raw_trans2.h',
+ private_library=True,
+ )
+
+bld.RECURSE('smb2')
+bld.RECURSE('rap')