diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source4/libcli | |
parent | Initial commit. (diff) | |
download | samba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip |
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
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, + ¶m, + 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, + ¶m, + 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') |