summaryrefslogtreecommitdiff
path: root/source4/libcli/raw
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2003-08-13 01:53:07 +0000
committerAndrew Tridgell <tridge@samba.org>2003-08-13 01:53:07 +0000
commitef2e26c91b80556af033d3335e55f5dfa6fff31d (patch)
treefaa21bfd7e7b5247250b47c7891dc1a5ebee6be9 /source4/libcli/raw
downloadsamba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.tar.gz
samba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.tar.bz2
samba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.zip
first public release of samba4 code
(This used to be commit b0510b5428b3461aeb9bbe3cc95f62fc73e2b97f)
Diffstat (limited to 'source4/libcli/raw')
-rw-r--r--source4/libcli/raw/README5
-rw-r--r--source4/libcli/raw/clikrb5.c399
-rw-r--r--source4/libcli/raw/clioplock.c57
-rw-r--r--source4/libcli/raw/clirewrite.c22
-rw-r--r--source4/libcli/raw/clisession.c444
-rw-r--r--source4/libcli/raw/clisocket.c148
-rw-r--r--source4/libcli/raw/clispnego.c533
-rw-r--r--source4/libcli/raw/clitransport.c218
-rw-r--r--source4/libcli/raw/clitree.c290
-rw-r--r--source4/libcli/raw/raweas.c147
-rw-r--r--source4/libcli/raw/rawfile.c687
-rw-r--r--source4/libcli/raw/rawfileinfo.c527
-rw-r--r--source4/libcli/raw/rawfsinfo.c282
-rw-r--r--source4/libcli/raw/rawioctl.c118
-rw-r--r--source4/libcli/raw/rawnegotiate.c157
-rw-r--r--source4/libcli/raw/rawnotify.c116
-rw-r--r--source4/libcli/raw/rawreadwrite.c321
-rw-r--r--source4/libcli/raw/rawrequest.c1019
-rw-r--r--source4/libcli/raw/rawsearch.c569
-rw-r--r--source4/libcli/raw/rawsetfileinfo.c335
-rw-r--r--source4/libcli/raw/rawtrans.c489
-rw-r--r--source4/libcli/raw/smb_signing.c341
22 files changed, 7224 insertions, 0 deletions
diff --git a/source4/libcli/raw/README b/source4/libcli/raw/README
new file mode 100644
index 0000000000..cb3e507e3a
--- /dev/null
+++ b/source4/libcli/raw/README
@@ -0,0 +1,5 @@
+Design notes for client library restructure:
+
+1 - no references to cli_state should exist in libcli/raw.
+2 - all interfaces to functions in this directory should use cli_session or cli_tree as
+ the primary context structure \ No newline at end of file
diff --git a/source4/libcli/raw/clikrb5.c b/source4/libcli/raw/clikrb5.c
new file mode 100644
index 0000000000..5edc56daa9
--- /dev/null
+++ b/source4/libcli/raw/clikrb5.c
@@ -0,0 +1,399 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5 routines for active directory
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Luke Howard 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_KRB5
+
+#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
+#define KRB5_KEY_TYPE(k) ((k)->keytype)
+#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length)
+#define KRB5_KEY_DATA(k) ((k)->keyvalue.data)
+#else
+#define KRB5_KEY_TYPE(k) ((k)->enctype)
+#define KRB5_KEY_LENGTH(k) ((k)->length)
+#define KRB5_KEY_DATA(k) ((k)->contents)
+#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
+
+#ifndef HAVE_KRB5_SET_REAL_TIME
+/*
+ * This function is not in the Heimdal mainline.
+ */
+ krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds)
+{
+ krb5_error_code ret;
+ int32_t sec, usec;
+
+ ret = krb5_us_timeofday(context, &sec, &usec);
+ if (ret)
+ return ret;
+
+ context->kdc_sec_offset = seconds - sec;
+ context->kdc_usec_offset = microseconds - usec;
+
+ return 0;
+}
+#endif
+
+#if defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) && !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
+ krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
+{
+ return krb5_set_default_in_tkt_etypes(ctx, enc);
+}
+#endif
+
+#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
+/* HEIMDAL */
+ void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
+{
+ pkaddr->addr_type = KRB5_ADDRESS_INET;
+ pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
+ pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
+}
+#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
+/* MIT */
+ void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
+{
+ pkaddr->addrtype = ADDRTYPE_INET;
+ pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
+ pkaddr->contents = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
+}
+#else
+ __ERROR__XX__UNKNOWN_ADDRTYPE
+#endif
+
+#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY)
+ int create_kerberos_key_from_string(krb5_context context,
+ krb5_principal host_princ,
+ krb5_data *password,
+ krb5_keyblock *key,
+ krb5_enctype enctype)
+{
+ int ret;
+ krb5_data salt;
+ krb5_encrypt_block eblock;
+
+ ret = krb5_principal2salt(context, host_princ, &salt);
+ if (ret) {
+ DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
+ return ret;
+ }
+ krb5_use_enctype(context, &eblock, enctype);
+ return krb5_string_to_key(context, &eblock, key, password, &salt);
+}
+#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
+ int create_kerberos_key_from_string(krb5_context context,
+ krb5_principal host_princ,
+ krb5_data *password,
+ krb5_keyblock *key,
+ krb5_enctype enctype)
+{
+ int ret;
+ krb5_salt salt;
+
+ ret = krb5_get_pw_salt(context, host_princ, &salt);
+ if (ret) {
+ DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
+ return ret;
+ }
+ return krb5_string_to_key_salt(context, enctype, password->data,
+ salt, key);
+}
+#else
+ __ERROR_XX_UNKNOWN_CREATE_KEY_FUNCTIONS
+#endif
+
+#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
+krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
+ krb5_enctype **enctypes)
+{
+ return krb5_get_permitted_enctypes(context, enctypes);
+}
+#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
+krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
+ krb5_enctype **enctypes)
+{
+ return krb5_get_default_in_tkt_etypes(context, enctypes);
+}
+#else
+#error UNKNOWN_GET_ENCTYPES_FUNCTIONS
+#endif
+
+ void free_kerberos_etypes(krb5_context context,
+ krb5_enctype *enctypes)
+{
+#if defined(HAVE_KRB5_FREE_KTYPES)
+ krb5_free_ktypes(context, enctypes);
+ return;
+#else
+ SAFE_FREE(enctypes);
+ return;
+#endif
+}
+
+#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
+ krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ return krb5_auth_con_setkey(context, auth_context, keyblock);
+}
+#endif
+
+ void get_auth_data_from_tkt(DATA_BLOB *auth_data, krb5_ticket *tkt)
+{
+#if defined(HAVE_KRB5_TKT_ENC_PART2)
+ if (tkt->enc_part2)
+ *auth_data = data_blob(tkt->enc_part2->authorization_data[0]->contents,
+ tkt->enc_part2->authorization_data[0]->length);
+#else
+ if (tkt->ticket.authorization_data && tkt->ticket.authorization_data->len)
+ *auth_data = data_blob(tkt->ticket.authorization_data->val->ad_data.data,
+ tkt->ticket.authorization_data->val->ad_data.length);
+#endif
+}
+
+ krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt)
+{
+#if defined(HAVE_KRB5_TKT_ENC_PART2)
+ return tkt->enc_part2->client;
+#else
+ return tkt->client;
+#endif
+}
+
+#if !defined(HAVE_KRB5_LOCATE_KDC)
+ krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters)
+{
+ krb5_krbhst_handle hnd;
+ krb5_krbhst_info *hinfo;
+ krb5_error_code rc;
+ int num_kdcs, i;
+ struct sockaddr *sa;
+
+ *addr_pp = NULL;
+ *naddrs = 0;
+
+ rc = krb5_krbhst_init(ctx, realm->data, KRB5_KRBHST_KDC, &hnd);
+ if (rc) {
+ DEBUG(0, ("krb5_locate_kdc: krb5_krbhst_init failed (%s)\n", error_message(rc)));
+ return rc;
+ }
+
+ for ( num_kdcs = 0; (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); num_kdcs++)
+ ;
+
+ krb5_krbhst_reset(ctx, hnd);
+
+ if (!num_kdcs) {
+ DEBUG(0, ("krb5_locate_kdc: zero kdcs found !\n"));
+ krb5_krbhst_free(ctx, hnd);
+ return -1;
+ }
+
+ sa = malloc( sizeof(struct sockaddr) * num_kdcs );
+ if (!sa) {
+ DEBUG(0, ("krb5_locate_kdc: malloc failed\n"));
+ krb5_krbhst_free(ctx, hnd);
+ naddrs = 0;
+ return -1;
+ }
+
+ memset(*addr_pp, '\0', sizeof(struct sockaddr) * num_kdcs );
+
+ for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) {
+ if (hinfo->ai->ai_family == AF_INET)
+ memcpy(&sa[i], hinfo->ai->ai_addr, sizeof(struct sockaddr));
+ }
+
+ krb5_krbhst_free(ctx, hnd);
+
+ *naddrs = num_kdcs;
+ *addr_pp = sa;
+ return 0;
+}
+#endif
+
+/*
+ we can't use krb5_mk_req because w2k wants the service to be in a particular format
+*/
+static krb5_error_code krb5_mk_req2(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ const char *principal,
+ krb5_ccache ccache,
+ krb5_data *outbuf)
+{
+ krb5_error_code retval;
+ krb5_principal server;
+ krb5_creds * credsp;
+ krb5_creds creds;
+ krb5_data in_data;
+
+ retval = krb5_parse_name(context, principal, &server);
+ if (retval) {
+ DEBUG(1,("Failed to parse principal %s\n", principal));
+ return retval;
+ }
+
+ /* obtain ticket & session key */
+ memset((char *)&creds, 0, sizeof(creds));
+ if ((retval = krb5_copy_principal(context, server, &creds.server))) {
+ DEBUG(1,("krb5_copy_principal failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_princ;
+ }
+
+ if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
+ DEBUG(1,("krb5_cc_get_principal failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_creds;
+ }
+
+ if ((retval = krb5_get_credentials(context, 0,
+ ccache, &creds, &credsp))) {
+ DEBUG(1,("krb5_get_credentials failed for %s (%s)\n",
+ principal, error_message(retval)));
+ goto cleanup_creds;
+ }
+
+ /* cope with the ticket being in the future due to clock skew */
+ if ((unsigned)credsp->times.starttime > time(NULL)) {
+ time_t t = time(NULL);
+ int time_offset = (unsigned)credsp->times.starttime - t;
+ DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
+ krb5_set_real_time(context, t + time_offset + 1, 0);
+ }
+
+ in_data.length = 0;
+ retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
+ &in_data, credsp, outbuf);
+ if (retval) {
+ DEBUG(1,("krb5_mk_req_extended failed (%s)\n",
+ error_message(retval)));
+ }
+
+ krb5_free_creds(context, credsp);
+
+cleanup_creds:
+ krb5_free_cred_contents(context, &creds);
+
+cleanup_princ:
+ krb5_free_principal(context, server);
+
+ return retval;
+}
+
+/*
+ get a kerberos5 ticket for the given service
+*/
+DATA_BLOB krb5_get_ticket(const char *principal, time_t time_offset)
+{
+ krb5_error_code retval;
+ krb5_data packet;
+ krb5_ccache ccdef;
+ krb5_context context;
+ krb5_auth_context auth_context = NULL;
+ DATA_BLOB ret;
+ krb5_enctype enc_types[] = {
+#ifdef ENCTYPE_ARCFOUR_HMAC
+ ENCTYPE_ARCFOUR_HMAC,
+#endif
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_NULL};
+
+ retval = krb5_init_context(&context);
+ if (retval) {
+ DEBUG(1,("krb5_init_context failed (%s)\n",
+ error_message(retval)));
+ goto failed;
+ }
+
+ if (time_offset != 0) {
+ krb5_set_real_time(context, time(NULL) + time_offset, 0);
+ }
+
+ if ((retval = krb5_cc_default(context, &ccdef))) {
+ DEBUG(1,("krb5_cc_default failed (%s)\n",
+ error_message(retval)));
+ goto failed;
+ }
+
+ if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) {
+ DEBUG(1,("krb5_set_default_tgs_ktypes failed (%s)\n",
+ error_message(retval)));
+ goto failed;
+ }
+
+ if ((retval = krb5_mk_req2(context,
+ &auth_context,
+ 0,
+ principal,
+ ccdef, &packet))) {
+ goto failed;
+ }
+
+ ret = data_blob(packet.data, packet.length);
+/* Hmm, heimdal dooesn't have this - what's the correct call? */
+/* krb5_free_data_contents(context, &packet); */
+ krb5_free_context(context);
+ return ret;
+
+failed:
+ if ( context )
+ krb5_free_context(context);
+
+ return data_blob(NULL, 0);
+}
+
+ BOOL krb5_get_smb_session_key(krb5_context context, krb5_auth_context auth_context, uint8 session_key[16])
+ {
+#ifdef ENCTYPE_ARCFOUR_HMAC
+ krb5_keyblock *skey;
+#endif
+ BOOL ret = False;
+
+ memset(session_key, 0, 16);
+
+#ifdef ENCTYPE_ARCFOUR_HMAC
+ if (krb5_auth_con_getremotesubkey(context, auth_context, &skey) == 0 && skey != NULL) {
+ if (KRB5_KEY_TYPE(skey) ==
+ ENCTYPE_ARCFOUR_HMAC
+ && KRB5_KEY_LENGTH(skey) == 16) {
+ memcpy(session_key, KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
+ ret = True;
+ }
+ krb5_free_keyblock(context, skey);
+ }
+#endif /* ENCTYPE_ARCFOUR_HMAC */
+
+ return ret;
+ }
+#else /* HAVE_KRB5 */
+ /* this saves a few linking headaches */
+DATA_BLOB krb5_get_ticket(const char *principal, time_t time_offset)
+ {
+ DEBUG(0,("NO KERBEROS SUPPORT\n"));
+ return data_blob(NULL, 0);
+ }
+
+#endif
diff --git a/source4/libcli/raw/clioplock.c b/source4/libcli/raw/clioplock.c
new file mode 100644
index 0000000000..8f69716bda
--- /dev/null
+++ b/source4/libcli/raw/clioplock.c
@@ -0,0 +1,57 @@
+/*
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+send an ack for an oplock break request
+****************************************************************************/
+BOOL cli_oplock_ack(struct cli_tree *tree, uint16 fnum, uint16 ack_level)
+{
+ BOOL ret;
+ struct cli_request *req;
+
+ req = cli_request_setup(tree, SMBlockingX, 8, 0);
+
+ SSVAL(req->out.vwv,VWV(0),0xFF);
+ SSVAL(req->out.vwv,VWV(1),0);
+ SSVAL(req->out.vwv,VWV(2),fnum);
+ SSVAL(req->out.vwv,VWV(3),ack_level);
+ SIVAL(req->out.vwv,VWV(4),0);
+ SSVAL(req->out.vwv,VWV(6),0);
+ SSVAL(req->out.vwv,VWV(7),0);
+
+ ret = cli_request_send(req);
+ cli_request_destroy(req);
+
+ return ret;
+}
+
+
+/****************************************************************************
+set the oplock handler for a connection
+****************************************************************************/
+void cli_oplock_handler(struct cli_transport *transport,
+ BOOL (*handler)(struct cli_transport *, uint16, uint16, uint8, void *),
+ void *private)
+{
+ transport->oplock.handler = handler;
+ transport->oplock.private = private;
+}
diff --git a/source4/libcli/raw/clirewrite.c b/source4/libcli/raw/clirewrite.c
new file mode 100644
index 0000000000..2d2e2e3feb
--- /dev/null
+++ b/source4/libcli/raw/clirewrite.c
@@ -0,0 +1,22 @@
+#include "includes.h"
+
+/*
+
+ this is a set of temporary stub functions used during the libsmb rewrite.
+ This file will need to go away before the rewrite is complete.
+*/
+
+void become_root(void)
+{}
+
+void unbecome_root(void)
+{}
+
+BOOL become_user_permanently(uid_t uid, gid_t gid)
+{ return True; }
+
+void set_effective_uid(uid_t uid)
+{}
+
+uid_t sec_initial_uid(void)
+{ return 0; }
diff --git a/source4/libcli/raw/clisession.c b/source4/libcli/raw/clisession.c
new file mode 100644
index 0000000000..406491e432
--- /dev/null
+++ b/source4/libcli/raw/clisession.c
@@ -0,0 +1,444 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client session context management functions
+ Copyright (C) Andrew Tridgell 1994-1998
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define SETUP_REQUEST_SESSION(cmd, wct, buflen) do { \
+ req = cli_request_setup_session(session, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+/****************************************************************************
+ Initialize the session context
+****************************************************************************/
+struct cli_session *cli_session_init(struct cli_transport *transport)
+{
+ struct cli_session *session;
+ TALLOC_CTX *mem_ctx = talloc_init("cli_session");
+ if (mem_ctx == NULL) {
+ return NULL;
+ }
+
+ session = talloc_zero(mem_ctx, sizeof(*session));
+ if (!session) {
+ talloc_destroy(mem_ctx);
+ return NULL;
+ }
+
+ session->mem_ctx = mem_ctx;
+ session->transport = transport;
+ session->pid = (uint16)getpid();
+ session->vuid = UID_FIELD_INVALID;
+ session->transport->reference_count++;
+
+ return session;
+}
+
+/****************************************************************************
+reduce reference_count and destroy is <= 0
+****************************************************************************/
+void cli_session_close(struct cli_session *session)
+{
+ session->reference_count--;
+ if (session->reference_count <= 0) {
+ cli_transport_close(session->transport);
+ talloc_destroy(session->mem_ctx);
+ }
+}
+
+/****************************************************************************
+ Perform a session setup (async send)
+****************************************************************************/
+struct cli_request *smb_raw_session_setup_send(struct cli_session *session, union smb_sesssetup *parms)
+{
+ struct cli_request *req;
+
+ switch (parms->generic.level) {
+ case RAW_SESSSETUP_GENERIC:
+ /* handled elsewhere */
+ return NULL;
+
+ 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);
+ cli_req_append_blob(req, &parms->old.in.password);
+ cli_req_append_string(req, parms->old.in.user, STR_TERMINATE);
+ cli_req_append_string(req, parms->old.in.domain, STR_TERMINATE|STR_UPPER);
+ cli_req_append_string(req, parms->old.in.os, STR_TERMINATE);
+ cli_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);
+ cli_req_append_blob(req, &parms->nt1.in.password1);
+ cli_req_append_blob(req, &parms->nt1.in.password2);
+ cli_req_append_string(req, parms->nt1.in.user, STR_TERMINATE);
+ cli_req_append_string(req, parms->nt1.in.domain, STR_TERMINATE|STR_UPPER);
+ cli_req_append_string(req, parms->nt1.in.os, STR_TERMINATE);
+ cli_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(10), parms->spnego.in.capabilities);
+ cli_req_append_blob(req, &parms->spnego.in.secblob);
+ cli_req_append_string(req, parms->spnego.in.os, STR_TERMINATE);
+ cli_req_append_string(req, parms->spnego.in.lanman, STR_TERMINATE);
+ break;
+ }
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ Perform a session setup (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_session_setup_recv(struct cli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_sesssetup *parms)
+{
+ uint16 len;
+ char *p;
+
+ if (!cli_request_receive(req)) {
+ return cli_request_destroy(req);
+ }
+
+ if (!NT_STATUS_IS_OK(req->status) &&
+ !NT_STATUS_EQUAL(req->status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ return cli_request_destroy(req);
+ }
+
+ switch (parms->generic.level) {
+ case RAW_SESSSETUP_GENERIC:
+ /* handled elsewhere */
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_SESSSETUP_OLD:
+ CLI_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 += cli_req_pull_string(req, mem_ctx, &parms->old.out.os, p, -1, STR_TERMINATE);
+ p += cli_req_pull_string(req, mem_ctx, &parms->old.out.lanman, p, -1, STR_TERMINATE);
+ p += cli_req_pull_string(req, mem_ctx, &parms->old.out.domain, p, -1, STR_TERMINATE);
+ }
+ break;
+
+ case RAW_SESSSETUP_NT1:
+ CLI_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 += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.os, p, -1, STR_TERMINATE);
+ p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.lanman, p, -1, STR_TERMINATE);
+ if (p < (req->in.data + req->in.data_size)) {
+ p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.domain, p, -1, STR_TERMINATE);
+ }
+ }
+ break;
+
+ case RAW_SESSSETUP_SPNEGO:
+ CLI_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 = cli_req_pull_blob(req, mem_ctx, p, len);
+ p += parms->spnego.out.secblob.length;
+ p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.os, p, -1, STR_TERMINATE);
+ p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.lanman, p, -1, STR_TERMINATE);
+ p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.domain, p, -1, STR_TERMINATE);
+ break;
+ }
+
+failed:
+ return cli_request_destroy(req);
+}
+
+/*
+ form an encrypted lanman password from a plaintext password
+ and the server supplied challenge
+*/
+static DATA_BLOB lanman_blob(const char *pass, DATA_BLOB challenge)
+{
+ DATA_BLOB blob = data_blob(NULL, 24);
+ SMBencrypt(pass, challenge.data, blob.data);
+ return blob;
+}
+
+/*
+ form an encrypted NT password from a plaintext password
+ and the server supplied challenge
+*/
+static DATA_BLOB nt_blob(const char *pass, DATA_BLOB challenge)
+{
+ DATA_BLOB blob = data_blob(NULL, 24);
+ SMBNTencrypt(pass, challenge.data, blob.data);
+ return blob;
+}
+
+/*
+ setup signing for a NT1 style session setup
+*/
+static void setup_nt1_signing(struct cli_transport *transport, const char *password)
+{
+ uchar nt_hash[16];
+ uchar session_key[16];
+ DATA_BLOB nt_response;
+
+ E_md4hash(password, nt_hash);
+ SMBsesskeygen_ntv1(nt_hash, NULL, session_key);
+ nt_response = nt_blob(password, transport->negotiate.secblob);
+
+ cli_transport_simple_set_signing(transport, session_key, nt_response);
+}
+
+/****************************************************************************
+ Perform a session setup (sync interface) using generic interface and the old
+ style sesssetup call
+****************************************************************************/
+static NTSTATUS smb_raw_session_setup_generic_old(struct cli_session *session,
+ TALLOC_CTX *mem_ctx,
+ union smb_sesssetup *parms)
+{
+ NTSTATUS status;
+ union smb_sesssetup s2;
+
+ /* use the old interface */
+ s2.generic.level = RAW_SESSSETUP_OLD;
+ s2.old.in.bufsize = ~0;
+ s2.old.in.mpx_max = 50;
+ s2.old.in.vc_num = 1;
+ s2.old.in.sesskey = parms->generic.in.sesskey;
+ s2.old.in.domain = parms->generic.in.domain;
+ s2.old.in.user = parms->generic.in.user;
+ s2.old.in.os = "Unix";
+ s2.old.in.lanman = "Samba";
+
+ if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
+ s2.old.in.password = lanman_blob(parms->generic.in.password,
+ session->transport->negotiate.secblob);
+ } else {
+ s2.old.in.password = data_blob(parms->generic.in.password,
+ strlen(parms->generic.in.password));
+ }
+
+ status = smb_raw_session_setup(session, mem_ctx, &s2);
+
+ data_blob_free(&s2.old.in.password);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ parms->generic.out.vuid = s2.old.out.vuid;
+ parms->generic.out.os = s2.old.out.os;
+ parms->generic.out.lanman = s2.old.out.lanman;
+ parms->generic.out.domain = s2.old.out.domain;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Perform a session setup (sync interface) using generic interface and the NT1
+ style sesssetup call
+****************************************************************************/
+static NTSTATUS smb_raw_session_setup_generic_nt1(struct cli_session *session,
+ TALLOC_CTX *mem_ctx,
+ union smb_sesssetup *parms)
+{
+ NTSTATUS status;
+ union smb_sesssetup s2;
+
+ s2.generic.level = RAW_SESSSETUP_NT1;
+ s2.nt1.in.bufsize = ~0;
+ s2.nt1.in.mpx_max = 50;
+ s2.nt1.in.vc_num = 1;
+ s2.nt1.in.sesskey = parms->generic.in.sesskey;
+ s2.nt1.in.capabilities = parms->generic.in.capabilities;
+ s2.nt1.in.domain = parms->generic.in.domain;
+ s2.nt1.in.user = parms->generic.in.user;
+ s2.nt1.in.os = "Unix";
+ s2.nt1.in.lanman = "Samba";
+
+ if (session->transport->negotiate.sec_mode &
+ NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
+ s2.nt1.in.password1 = lanman_blob(parms->generic.in.password,
+ session->transport->negotiate.secblob);
+ s2.nt1.in.password2 = nt_blob(parms->generic.in.password,
+ session->transport->negotiate.secblob);
+ setup_nt1_signing(session->transport, parms->generic.in.password);
+ } else {
+ s2.nt1.in.password1 = data_blob(parms->generic.in.password,
+ strlen(parms->generic.in.password));
+ s2.nt1.in.password2 = data_blob(NULL, 0);
+ }
+
+ status = smb_raw_session_setup(session, mem_ctx, &s2);
+
+ data_blob_free(&s2.nt1.in.password1);
+ data_blob_free(&s2.nt1.in.password2);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ parms->generic.out.vuid = s2.nt1.out.vuid;
+ parms->generic.out.os = s2.nt1.out.os;
+ parms->generic.out.lanman = s2.nt1.out.lanman;
+ parms->generic.out.domain = s2.nt1.out.domain;
+
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Perform a session setup (sync interface) using generic interface
+****************************************************************************/
+static NTSTATUS smb_raw_session_setup_generic(struct cli_session *session,
+ TALLOC_CTX *mem_ctx,
+ union smb_sesssetup *parms)
+{
+ if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
+ /* no session setup at all in earliest protocols */
+ ZERO_STRUCT(parms->generic.out);
+ return NT_STATUS_OK;
+ }
+
+ /* see if we need to use the original session setup interface */
+ if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
+ return smb_raw_session_setup_generic_old(session, mem_ctx, parms);
+ }
+
+ /* see if we should use the NT1 interface */
+ if (!(session->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) ||
+ !session->transport->options.use_spnego) {
+ return smb_raw_session_setup_generic_nt1(session, mem_ctx, parms);
+ }
+
+ /* default to using SPNEGO/NTLMSSP */
+ DEBUG(0,("Need to add client SPNEGO code back in\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+
+/****************************************************************************
+ Perform a session setup (sync interface)
+this interface allows for RAW_SESSSETUP_GENERIC to auto-select session
+setup varient based on negotiated protocol options
+****************************************************************************/
+NTSTATUS smb_raw_session_setup(struct cli_session *session, TALLOC_CTX *mem_ctx,
+ union smb_sesssetup *parms)
+{
+ struct cli_request *req;
+
+ if (parms->generic.level == RAW_SESSSETUP_GENERIC) {
+ return smb_raw_session_setup_generic(session, mem_ctx, parms);
+ }
+
+ req = smb_raw_session_setup_send(session, parms);
+ return smb_raw_session_setup_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ Send a uloggoff (async send)
+*****************************************************************************/
+struct cli_request *smb_raw_ulogoff_send(struct cli_session *session)
+{
+ struct cli_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 (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Send a uloggoff (sync interface)
+*****************************************************************************/
+NTSTATUS smb_raw_ulogoff(struct cli_session *session)
+{
+ struct cli_request *req = smb_raw_ulogoff_send(session);
+ return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Send a SMBexit
+****************************************************************************/
+NTSTATUS smb_raw_exit(struct cli_session *session)
+{
+ struct cli_request *req;
+
+ req = cli_request_setup_session(session, SMBexit, 0, 0);
+
+ if (cli_request_send(req)) {
+ cli_request_receive(req);
+ }
+ return cli_request_destroy(req);
+}
diff --git a/source4/libcli/raw/clisocket.c b/source4/libcli/raw/clisocket.c
new file mode 100644
index 0000000000..f0e05085c4
--- /dev/null
+++ b/source4/libcli/raw/clisocket.c
@@ -0,0 +1,148 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client socket context management functions
+ Copyright (C) Andrew Tridgell 1994-1998
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+
+/*
+ create a cli_socket context
+*/
+struct cli_socket *cli_sock_init(void)
+{
+ struct cli_socket *sock;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("cli_socket");
+ if (!mem_ctx) return NULL;
+
+ sock = talloc_zero(mem_ctx, sizeof(*sock));
+ if (!sock) {
+ talloc_destroy(mem_ctx);
+ return NULL;
+ }
+
+ sock->mem_ctx = mem_ctx;
+ sock->fd = -1;
+ sock->port = 445;
+ /* 20 second default timeout */
+ sock->timeout = 20000;
+
+ return sock;
+}
+
+/*
+ connect a cli_socket context to an IP/port pair
+ if port is 0 then choose 445 then 139
+*/
+BOOL cli_sock_connect(struct cli_socket *sock, struct in_addr *ip, int port)
+{
+ if (getenv("LIBSMB_PROG")) {
+ sock->fd = sock_exec(getenv("LIBSMB_PROG"));
+ return sock->fd != -1;
+ }
+
+ if (port == 0) {
+ return cli_sock_connect(sock, ip, 445) ||
+ cli_sock_connect(sock, ip, 139);
+ }
+
+ sock->dest_ip = *ip;
+ sock->port = port;
+ sock->fd = open_socket_out(SOCK_STREAM,
+ &sock->dest_ip,
+ sock->port,
+ LONG_CONNECT_TIMEOUT);
+ return (sock->fd != -1);
+}
+
+
+/****************************************************************************
+ reduce socket reference count - if it becomes zero then close
+****************************************************************************/
+void cli_sock_close(struct cli_socket *sock)
+{
+ sock->reference_count--;
+ if (sock->reference_count <= 0 && sock->fd != -1) {
+ close(sock->fd);
+ sock->fd = -1;
+ }
+}
+
+/****************************************************************************
+ Set socket options on a open connection.
+****************************************************************************/
+void cli_sock_set_options(struct cli_socket *sock, const char *options)
+{
+ set_socket_options(sock->fd, options);
+}
+
+/****************************************************************************
+ Write to socket. Return amount written.
+****************************************************************************/
+ssize_t cli_sock_write(struct cli_socket *sock, const char *data, size_t len)
+{
+ return write_data(sock->fd, data, len);
+}
+
+
+/****************************************************************************
+ Read from socket. return amount read
+****************************************************************************/
+ssize_t cli_sock_read(struct cli_socket *sock, char *data, size_t len)
+{
+ return read_data(sock->fd, data, len);
+}
+
+/****************************************************************************
+resolve a hostname and connect
+****************************************************************************/
+BOOL cli_sock_connect_byname(struct cli_socket *sock, const char *host, int port)
+{
+ int name_type = 0x20;
+ struct in_addr ip;
+ TALLOC_CTX *mem_ctx;
+ char *name, *p;
+
+ if (getenv("LIBSMB_PROG")) {
+ sock->fd = sock_exec(getenv("LIBSMB_PROG"));
+ return sock->fd != -1;
+ }
+
+ mem_ctx = talloc_init("cli_sock_connect_byname");
+ if (!mem_ctx) return False;
+
+ name = talloc_strdup(mem_ctx, host);
+
+ /* allow hostnames of the form NAME#xx and do a netbios lookup */
+ if ((p = strchr(name, '#'))) {
+ name_type = strtol(p+1, NULL, 16);
+ *p = 0;
+ }
+
+ if (!resolve_name(mem_ctx, name, &ip, name_type)) {
+ talloc_destroy(mem_ctx);
+ return False;
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return cli_sock_connect(sock, &ip, port);
+}
diff --git a/source4/libcli/raw/clispnego.c b/source4/libcli/raw/clispnego.c
new file mode 100644
index 0000000000..53f7eb6e7d
--- /dev/null
+++ b/source4/libcli/raw/clispnego.c
@@ -0,0 +1,533 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5/SPNEGO routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jim McDonough 2002
+ Copyright (C) Luke Howard 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ generate a negTokenInit packet given a GUID, a list of supported
+ OIDs (the mechanisms) and a principal name string
+*/
+DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16],
+ const char *OIDs[],
+ const char *principal)
+{
+ int i;
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_write(&data, guid, 16);
+ asn1_push_tag(&data,ASN1_APPLICATION(0));
+ asn1_write_OID(&data,OID_SPNEGO);
+ asn1_push_tag(&data,ASN1_CONTEXT(0));
+ asn1_push_tag(&data,ASN1_SEQUENCE(0));
+
+ asn1_push_tag(&data,ASN1_CONTEXT(0));
+ asn1_push_tag(&data,ASN1_SEQUENCE(0));
+ for (i=0; OIDs[i]; i++) {
+ asn1_write_OID(&data,OIDs[i]);
+ }
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_write_GeneralString(&data,principal);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ }
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+
+ return ret;
+}
+
+/*
+ Generate a negTokenInit as used by the client side ... It has a mechType
+ (OID), and a mechToken (a security blob) ...
+
+ Really, we need to break out the NTLMSSP stuff as well, because it could be
+ raw in the packets!
+*/
+DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob)
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data, ASN1_APPLICATION(0));
+ asn1_write_OID(&data,OID_SPNEGO);
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_write_OID(&data, OID);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data,blob.data,blob.length);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ }
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+
+ return ret;
+}
+
+/*
+ parse a negTokenInit packet giving a GUID, a list of supported
+ OIDs (the mechanisms) and a principal name string
+*/
+BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
+ char *OIDs[ASN1_MAX_OIDS],
+ char **principal)
+{
+ int i;
+ BOOL ret;
+ ASN1_DATA data;
+
+ asn1_load(&data, blob);
+
+ asn1_start_tag(&data,ASN1_APPLICATION(0));
+ asn1_check_OID(&data,OID_SPNEGO);
+ asn1_start_tag(&data,ASN1_CONTEXT(0));
+ asn1_start_tag(&data,ASN1_SEQUENCE(0));
+
+ asn1_start_tag(&data,ASN1_CONTEXT(0));
+ asn1_start_tag(&data,ASN1_SEQUENCE(0));
+ for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
+ char *oid = NULL;
+ asn1_read_OID(&data,&oid);
+ OIDs[i] = oid;
+ }
+ OIDs[i] = NULL;
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data, ASN1_CONTEXT(3));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_read_GeneralString(&data,principal);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_end_tag(&data);
+
+ ret = !data.has_error;
+ asn1_free(&data);
+ return ret;
+}
+
+
+/*
+ generate a negTokenTarg packet given a list of OIDs and a security blob
+*/
+DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
+{
+ int i;
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data, ASN1_APPLICATION(0));
+ asn1_write_OID(&data,OID_SPNEGO);
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ for (i=0; OIDs[i]; i++) {
+ asn1_write_OID(&data,OIDs[i]);
+ }
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data,blob.data,blob.length);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ }
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+
+ return ret;
+}
+
+
+/*
+ parse a negTokenTarg packet giving a list of OIDs and a security blob
+*/
+BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
+{
+ int i;
+ ASN1_DATA data;
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_APPLICATION(0));
+ asn1_check_OID(&data,OID_SPNEGO);
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
+ char *oid = NULL;
+ asn1_read_OID(&data,&oid);
+ OIDs[i] = oid;
+ }
+ OIDs[i] = NULL;
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data, ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data,secblob);
+ asn1_end_tag(&data);
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_end_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ return False;
+ }
+
+ asn1_free(&data);
+ return True;
+}
+
+/*
+ generate a krb5 GSS-API wrapper packet given a ticket
+*/
+DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket, const uint8 tok_id[2])
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data, ASN1_APPLICATION(0));
+ asn1_write_OID(&data, OID_KERBEROS5);
+
+ asn1_write(&data, tok_id, 2);
+ asn1_write(&data, ticket.data, ticket.length);
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ }
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+
+ return ret;
+}
+
+/*
+ parse a krb5 GSS-API wrapper packet giving a ticket
+*/
+BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
+{
+ BOOL ret;
+ ASN1_DATA data;
+ int data_remaining;
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_APPLICATION(0));
+ asn1_check_OID(&data, OID_KERBEROS5);
+
+ data_remaining = asn1_tag_remaining(&data);
+
+ if (data_remaining < 3) {
+ data.has_error = True;
+ } else {
+ asn1_read(&data, tok_id, 2);
+ data_remaining -= 2;
+ *ticket = data_blob(NULL, data_remaining);
+ asn1_read(&data, ticket->data, ticket->length);
+ }
+
+ asn1_end_tag(&data);
+
+ ret = !data.has_error;
+
+ asn1_free(&data);
+
+ return ret;
+}
+
+
+/*
+ generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
+ kerberos session setup
+*/
+DATA_BLOB spnego_gen_negTokenTarg(const char *principal, int time_offset)
+{
+ DATA_BLOB tkt, tkt_wrapped, targ;
+ const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
+
+ /* get a kerberos ticket for the service */
+ tkt = krb5_get_ticket(principal, time_offset);
+
+ /* wrap that up in a nice GSS-API wrapping */
+ tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
+
+ /* and wrap that in a shiny SPNEGO wrapper */
+ targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
+
+ data_blob_free(&tkt_wrapped);
+ data_blob_free(&tkt);
+
+ return targ;
+}
+
+
+/*
+ parse a spnego NTLMSSP challenge packet giving two security blobs
+*/
+BOOL spnego_parse_challenge(const DATA_BLOB blob,
+ DATA_BLOB *chal1, DATA_BLOB *chal2)
+{
+ BOOL ret;
+ ASN1_DATA data;
+
+ ZERO_STRUCTP(chal1);
+ ZERO_STRUCTP(chal2);
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data,ASN1_CONTEXT(1));
+ asn1_start_tag(&data,ASN1_SEQUENCE(0));
+
+ asn1_start_tag(&data,ASN1_CONTEXT(0));
+ asn1_check_enumerated(&data,1);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data,ASN1_CONTEXT(1));
+ asn1_check_OID(&data, OID_NTLMSSP);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data,ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data, chal1);
+ asn1_end_tag(&data);
+
+ /* the second challenge is optional (XP doesn't send it) */
+ if (asn1_tag_remaining(&data)) {
+ asn1_start_tag(&data,ASN1_CONTEXT(3));
+ asn1_read_OctetString(&data, chal2);
+ asn1_end_tag(&data);
+ }
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ ret = !data.has_error;
+ asn1_free(&data);
+ return ret;
+}
+
+
+/*
+ generate a SPNEGO auth packet. This will contain the encrypted passwords
+*/
+DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data, ASN1_CONTEXT(1));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_push_tag(&data, ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data,blob.data,blob.length);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ ret = data_blob(data.data, data.length);
+
+ asn1_free(&data);
+
+ return ret;
+}
+
+/*
+ parse a SPNEGO auth packet. This contains the encrypted passwords
+*/
+BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
+{
+ ASN1_DATA data;
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_CONTEXT(1));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data,auth);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
+ asn1_free(&data);
+ return False;
+ }
+
+ asn1_free(&data);
+ return True;
+}
+
+/*
+ generate a minimal SPNEGO response packet. Doesn't contain much.
+*/
+DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
+ const char *mechOID)
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+ uint8 negResult;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ negResult = SPNEGO_NEG_RESULT_ACCEPT;
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
+ } else {
+ negResult = SPNEGO_NEG_RESULT_REJECT;
+ }
+
+ ZERO_STRUCT(data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(1));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_write_enumerated(&data, negResult);
+ asn1_pop_tag(&data);
+
+ if (reply->data != NULL) {
+ asn1_push_tag(&data,ASN1_CONTEXT(1));
+ asn1_write_OID(&data, mechOID);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data,ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data, reply->data, reply->length);
+ asn1_pop_tag(&data);
+ }
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+ return ret;
+}
+
+/*
+ parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
+*/
+BOOL spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status,
+ DATA_BLOB *auth)
+{
+ ASN1_DATA data;
+ uint8 negResult;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ negResult = SPNEGO_NEG_RESULT_ACCEPT;
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
+ } else {
+ negResult = SPNEGO_NEG_RESULT_REJECT;
+ }
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_CONTEXT(1));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_check_enumerated(&data, negResult);
+ asn1_end_tag(&data);
+
+ if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
+ asn1_start_tag(&data,ASN1_CONTEXT(1));
+ asn1_check_OID(&data, OID_NTLMSSP);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data,ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data, auth);
+ asn1_end_tag(&data);
+ }
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data.ofs));
+ asn1_free(&data);
+ data_blob_free(auth);
+ return False;
+ }
+
+ asn1_free(&data);
+ return True;
+}
+
diff --git a/source4/libcli/raw/clitransport.c b/source4/libcli/raw/clitransport.c
new file mode 100644
index 0000000000..80bb1e301f
--- /dev/null
+++ b/source4/libcli/raw/clitransport.c
@@ -0,0 +1,218 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client transport context management functions
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ create a transport structure based on an established socket
+*/
+struct cli_transport *cli_transport_init(struct cli_socket *sock)
+{
+ TALLOC_CTX *mem_ctx;
+ struct cli_transport *transport;
+
+ mem_ctx = talloc_init("cli_transport");
+ if (!mem_ctx) return NULL;
+
+ transport = talloc_zero(mem_ctx, sizeof(*transport));
+ if (!transport) return NULL;
+
+ transport->mem_ctx = mem_ctx;
+ transport->socket = sock;
+ transport->negotiate.protocol = PROTOCOL_NT1;
+ transport->negotiate.max_xmit = ~0;
+ cli_null_set_signing(transport);
+ transport->socket->reference_count++;
+
+ return transport;
+}
+
+/*
+ decrease reference count on a transport, and destroy if it becomes
+ zero
+*/
+void cli_transport_close(struct cli_transport *transport)
+{
+ transport->reference_count--;
+ if (transport->reference_count <= 0) {
+ cli_sock_close(transport->socket);
+ talloc_destroy(transport->mem_ctx);
+ }
+}
+
+
+
+/****************************************************************************
+send a session request (if appropriate)
+****************************************************************************/
+BOOL cli_transport_connect(struct cli_transport *transport,
+ struct nmb_name *calling,
+ struct nmb_name *called)
+{
+ char *p;
+ int len = NBT_HDR_SIZE;
+ struct cli_request *req;
+
+ /* 445 doesn't have session request */
+ if (transport->socket->port == 445) {
+ return True;
+ }
+
+ /* allocate output buffer */
+ req = cli_request_setup_nonsmb(transport, NBT_HDR_SIZE + 2*nbt_mangled_name_len());
+
+ /* put in the destination name */
+ p = req->out.buffer + NBT_HDR_SIZE;
+ name_mangle(called->name, p, called->name_type);
+ len += name_len(p);
+
+ /* and my name */
+ p = req->out.buffer+len;
+ name_mangle(calling->name, p, calling->name_type);
+ len += name_len(p);
+
+ _smb_setlen(req->out.buffer,len-4);
+ SCVAL(req->out.buffer,0,0x81);
+
+ if (!cli_request_send(req) ||
+ !cli_request_receive(req)) {
+ cli_request_destroy(req);
+ return False;
+ }
+
+ if (CVAL(req->in.buffer,0) != 0x82) {
+ transport->error.etype = ETYPE_NBT;
+ transport->error.e.nbt_error = CVAL(req->in.buffer,4);
+ cli_request_destroy(req);
+ return False;
+ }
+
+ cli_request_destroy(req);
+ return True;
+}
+
+
+/****************************************************************************
+get next mid in sequence
+****************************************************************************/
+uint16 cli_transport_next_mid(struct cli_transport *transport)
+{
+ uint16 mid;
+ struct cli_request *req;
+
+ mid = transport->next_mid;
+
+again:
+ /* now check to see if this mid is being used by one of the
+ pending requests. This is quite efficient because the list is
+ usually very short */
+
+ /* the zero mid is reserved for requests that don't have a mid */
+ if (mid == 0) mid = 1;
+
+ for (req=transport->pending_requests; req; req=req->next) {
+ if (req->mid == mid) {
+ mid++;
+ goto again;
+ }
+ }
+
+ transport->next_mid = mid+1;
+ return mid;
+}
+
+/*
+ setup the idle handler for a transport
+*/
+void cli_transport_idle_handler(struct cli_transport *transport,
+ void (*idle_func)(struct cli_transport *, void *),
+ uint_t period,
+ void *private)
+{
+ transport->idle.func = idle_func;
+ transport->idle.private = private;
+ transport->idle.period = period;
+}
+
+
+/*
+ determine if a packet is pending for receive on a transport
+*/
+BOOL cli_transport_pending(struct cli_transport *transport)
+{
+ return socket_pending(transport->socket->fd);
+}
+
+
+
+/*
+ wait for data on a transport, periodically calling a wait function
+ if one has been defined
+ return True if a packet is received
+*/
+BOOL cli_transport_select(struct cli_transport *transport)
+{
+ fd_set fds;
+ int selrtn;
+ int fd;
+ struct timeval timeout;
+
+ fd = transport->socket->fd;
+
+ if (fd == -1) {
+ return False;
+ }
+
+ do {
+ uint_t period = 1000;
+
+ FD_ZERO(&fds);
+ FD_SET(fd,&fds);
+
+ if (transport->idle.func) {
+ period = transport->idle.period;
+ }
+
+ timeout.tv_sec = period / 1000;
+ timeout.tv_usec = 1000*(period%1000);
+
+ selrtn = sys_select_intr(fd+1,&fds,NULL,NULL,&timeout);
+
+ if (selrtn == 1) {
+ /* the fd is readable */
+ return True;
+ }
+
+ if (selrtn == -1) {
+ /* sys_select_intr() already handles EINTR, so this
+ is an error. The socket is probably dead */
+ return False;
+ }
+
+ /* only other possibility is that we timed out - call the idle function
+ if there is one */
+ if (transport->idle.func) {
+ transport->idle.func(transport, transport->idle.private);
+ }
+ } while (selrtn == 0);
+
+ return True;
+}
diff --git a/source4/libcli/raw/clitree.c b/source4/libcli/raw/clitree.c
new file mode 100644
index 0000000000..2a41273913
--- /dev/null
+++ b/source4/libcli/raw/clitree.c
@@ -0,0 +1,290 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client tree context management functions
+ Copyright (C) Andrew Tridgell 1994-1998
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define SETUP_REQUEST_TREE(cmd, wct, buflen) do { \
+ req = cli_request_setup(tree, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+
+/****************************************************************************
+ Initialize the tree context
+****************************************************************************/
+struct cli_tree *cli_tree_init(struct cli_session *session)
+{
+ struct cli_tree *tree;
+ TALLOC_CTX *mem_ctx = talloc_init("cli_tree");
+ if (mem_ctx == NULL) {
+ return NULL;
+ }
+
+ tree = talloc_zero(mem_ctx, sizeof(*tree));
+ if (!tree) {
+ talloc_destroy(mem_ctx);
+ return NULL;
+ }
+
+ tree->mem_ctx = mem_ctx;
+ tree->session = session;
+ tree->session->reference_count++;
+
+ return tree;
+}
+
+/****************************************************************************
+reduce reference count on a tree and destroy if <= 0
+****************************************************************************/
+void cli_tree_close(struct cli_tree *tree)
+{
+ if (!tree) return;
+ tree->reference_count--;
+ if (tree->reference_count <= 0) {
+ cli_session_close(tree->session);
+ talloc_destroy(tree->mem_ctx);
+ }
+}
+
+
+/****************************************************************************
+ Send a tconX (async send)
+****************************************************************************/
+struct cli_request *smb_tree_connect_send(struct cli_tree *tree, union smb_tcon *parms)
+{
+ struct cli_request *req;
+
+ switch (parms->tcon.level) {
+ case RAW_TCON_TCON:
+ SETUP_REQUEST_TREE(SMBtcon, 0, 0);
+ cli_req_append_ascii4(req, parms->tcon.in.service, STR_ASCII);
+ cli_req_append_ascii4(req, parms->tcon.in.password,STR_ASCII);
+ cli_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);
+ cli_req_append_blob(req, &parms->tconx.in.password);
+ cli_req_append_string(req, parms->tconx.in.path, STR_TERMINATE | STR_UPPER);
+ cli_req_append_string(req, parms->tconx.in.device, STR_TERMINATE | STR_ASCII);
+ break;
+ }
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Send a tconX (async recv)
+****************************************************************************/
+NTSTATUS smb_tree_connect_recv(struct cli_request *req, TALLOC_CTX *mem_ctx, union smb_tcon *parms)
+{
+ char *p;
+
+ if (!cli_request_receive(req) ||
+ cli_request_is_error(req)) {
+ goto failed;
+ }
+
+ switch (parms->tcon.level) {
+ case RAW_TCON_TCON:
+ CLI_CHECK_WCT(req, 2);
+ parms->tcon.out.max_xmit = SVAL(req->in.vwv, VWV(0));
+ parms->tcon.out.cnum = SVAL(req->in.vwv, VWV(1));
+ break;
+
+ case RAW_TCON_TCONX:
+ ZERO_STRUCT(parms->tconx.out);
+ CLI_CHECK_MIN_WCT(req, 0); /* this depends on the protocol level */
+ parms->tconx.out.cnum = SVAL(req->in.hdr, HDR_TID);
+ if (req->in.wct >= 4) {
+ parms->tconx.out.options = SVAL(req->in.vwv, VWV(3));
+ }
+
+ /* output is actual service name */
+ p = req->in.data;
+ if (!p) break;
+
+ p += cli_req_pull_string(req, mem_ctx, &parms->tconx.out.dev_type,
+ p, -1, STR_ASCII | STR_TERMINATE);
+ p += cli_req_pull_string(req, mem_ctx, &parms->tconx.out.fs_type,
+ p, -1, STR_TERMINATE);
+ break;
+ }
+
+failed:
+ return cli_request_destroy(req);
+}
+
+/****************************************************************************
+ Send a tconX (sync interface)
+****************************************************************************/
+NTSTATUS smb_tree_connect(struct cli_tree *tree, TALLOC_CTX *mem_ctx, union smb_tcon *parms)
+{
+ struct cli_request *req = smb_tree_connect_send(tree, parms);
+ return smb_tree_connect_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ Send a tree disconnect.
+****************************************************************************/
+NTSTATUS smb_tree_disconnect(struct cli_tree *tree)
+{
+ struct cli_request *req;
+
+ if (!tree) return NT_STATUS_OK;
+ req = cli_request_setup(tree, SMBtdis, 0, 0);
+
+ if (cli_request_send(req)) {
+ cli_request_receive(req);
+ }
+ return cli_request_destroy(req);
+}
+
+
+/*
+ a convenient function to establish a cli_tree from scratch, using reasonable default
+ parameters
+*/
+NTSTATUS cli_tree_full_connection(struct cli_tree **ret_tree,
+ const char *my_name,
+ const char *dest_host, int port,
+ const char *service, const char *service_type,
+ const char *user, const char *domain,
+ const char *password)
+{
+ struct cli_socket *sock;
+ struct cli_transport *transport;
+ struct cli_session *session;
+ struct cli_tree *tree;
+ NTSTATUS status;
+ struct nmb_name calling;
+ struct nmb_name called;
+ union smb_sesssetup setup;
+ union smb_tcon tcon;
+ TALLOC_CTX *mem_ctx;
+
+ *ret_tree = NULL;
+
+ sock = cli_sock_init();
+ if (!sock) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* open a TCP socket to the server */
+ if (!cli_sock_connect_byname(sock, dest_host, port)) {
+ DEBUG(2,("Failed to establish socket connection - %s\n", strerror(errno)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ transport = cli_transport_init(sock);
+ if (!transport) {
+ cli_sock_close(sock);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* send a NBT session request, if applicable */
+ make_nmb_name(&calling, my_name, 0x0);
+ make_nmb_name(&called, dest_host, 0x20);
+
+ if (!cli_transport_connect(transport, &calling, &called)) {
+ cli_transport_close(transport);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+
+ /* negotiate protocol options with the server */
+ status = smb_raw_negotiate(transport);
+ if (!NT_STATUS_IS_OK(status)) {
+ cli_transport_close(transport);
+ return status;
+ }
+
+ session = cli_session_init(transport);
+ if (!session) {
+ cli_transport_close(transport);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* prepare a session setup to establish a security context */
+ setup.generic.level = RAW_SESSSETUP_GENERIC;
+ setup.generic.in.sesskey = transport->negotiate.sesskey;
+ setup.generic.in.capabilities = CAP_UNICODE | CAP_STATUS32 |
+ CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+ CAP_W2K_SMBS | CAP_LARGE_READX | CAP_LARGE_WRITEX;
+ setup.generic.in.password = password;
+ setup.generic.in.user = user;
+ setup.generic.in.domain = domain;
+
+ mem_ctx = talloc_init("tcon");
+ if (!mem_ctx) {
+ cli_tree_close(tree);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = smb_raw_session_setup(session, mem_ctx, &setup);
+ if (!NT_STATUS_IS_OK(status)) {
+ cli_session_close(session);
+ talloc_destroy(mem_ctx);
+ return status;
+ }
+
+ session->vuid = setup.generic.out.vuid;
+
+ tree = cli_tree_init(session);
+ if (!tree) {
+ cli_session_close(session);
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* connect to a share using a tree connect */
+ tcon.generic.level = RAW_TCON_TCONX;
+ tcon.tconx.in.flags = 0;
+ tcon.tconx.in.password = data_blob(NULL, 0);
+ tcon.tconx.in.path = service;
+ tcon.tconx.in.device = service_type;
+
+ status = smb_tree_connect(tree, mem_ctx, &tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ cli_tree_close(tree);
+ talloc_destroy(mem_ctx);
+ return status;
+ }
+
+ tree->tid = tcon.tconx.out.cnum;
+ tree->device = talloc_strdup(tree->mem_ctx, tcon.tconx.out.dev_type);
+ tree->fs_type = talloc_strdup(tree->mem_ctx, tcon.tconx.out.fs_type);
+
+ talloc_destroy(mem_ctx);
+
+ *ret_tree = tree;
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/raw/raweas.c b/source4/libcli/raw/raweas.c
new file mode 100644
index 0000000000..ce0368c304
--- /dev/null
+++ b/source4/libcli/raw/raweas.c
@@ -0,0 +1,147 @@
+/*
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.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
+*/
+uint_t ea_list_size(uint_t num_eas, struct ea_struct *eas)
+{
+ uint_t total = 4;
+ int i;
+ for (i=0;i<num_eas;i++) {
+ total += 4 + strlen(eas[i].name.s)+1 + eas[i].value.length;
+ }
+ return total;
+}
+
+/*
+ put a ea_list into a pre-allocated buffer - buffer must be at least
+ of size ea_list_size()
+*/
+void ea_put_list(char *data, uint_t num_eas, struct ea_struct *eas)
+{
+ int i;
+ uint32 ea_size;
+
+ ea_size = ea_list_size(num_eas, eas);
+
+ SIVAL(data, 0, ea_size);
+ data += 4;
+
+ for (i=0;i<num_eas;i++) {
+ uint_t nlen = strlen(eas[i].name.s);
+ SCVAL(data, 0, eas[i].flags);
+ SCVAL(data, 1, nlen);
+ SSVAL(data, 2, eas[i].value.length);
+ memcpy(data+4, eas[i].name.s, nlen+1);
+ memcpy(data+4+nlen+1, eas[i].value.data, eas[i].value.length);
+ data += 4+nlen+1+eas[i].value.length;
+ }
+}
+
+
+/*
+ pull a ea_struct from a buffer. Return the number of bytes consumed
+*/
+uint_t ea_pull_struct(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ struct ea_struct *ea)
+{
+ uint8 nlen;
+ uint16 vlen;
+
+ 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, blob->data+4, nlen);
+ ea->name.private_length = nlen;
+ ea->value = data_blob_talloc(mem_ctx, NULL, vlen+1);
+ if (!ea->value.data) return 0;
+ if (vlen) {
+ memcpy(ea->value.data, blob->data+4+nlen+1, vlen);
+ }
+ ea->value.data[vlen] = 0;
+ ea->value.length--;
+
+ return 4 + nlen+1 + vlen;
+}
+
+
+/*
+ pull a ea_list from a buffer
+*/
+NTSTATUS ea_pull_list(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ uint_t *num_eas, struct ea_struct **eas)
+{
+ int n;
+ uint32 ea_size, ofs;
+
+ if (blob->length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ ea_size = IVAL(blob->data, 0);
+ if (ea_size > blob->length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ofs = 4;
+ n = 0;
+ *num_eas = 0;
+ *eas = NULL;
+
+ while (ofs < ea_size) {
+ uint_t len;
+ DATA_BLOB blob2;
+
+ blob2.data = blob->data + ofs;
+ blob2.length = ea_size - ofs;
+
+ *eas = talloc_realloc(mem_ctx, *eas, sizeof(**eas) * (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;
+}
+
diff --git a/source4/libcli/raw/rawfile.c b/source4/libcli/raw/rawfile.c
new file mode 100644
index 0000000000..279dfcf0c1
--- /dev/null
+++ b/source4/libcli/raw/rawfile.c
@@ -0,0 +1,687 @@
+/*
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define SETUP_REQUEST(cmd, wct, buflen) do { \
+ req = cli_request_setup(tree, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+
+/****************************************************************************
+ Rename a file - async interface
+****************************************************************************/
+struct cli_request *smb_raw_rename_send(struct cli_tree *tree,
+ struct smb_rename *parms)
+{
+ struct cli_request *req;
+
+ SETUP_REQUEST(SMBmv, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), parms->in.attrib);
+
+ cli_req_append_ascii4(req, parms->in.pattern1, STR_TERMINATE);
+ cli_req_append_ascii4(req, parms->in.pattern2, STR_TERMINATE);
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Rename a file - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_rename(struct cli_tree *tree,
+ struct smb_rename *parms)
+{
+ struct cli_request *req = smb_raw_rename_send(tree, parms);
+ return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Delete a file - async interface
+****************************************************************************/
+struct cli_request *smb_raw_unlink_send(struct cli_tree *tree,
+ struct smb_unlink *parms)
+{
+ struct cli_request *req;
+
+ SETUP_REQUEST(SMBunlink, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), parms->in.attrib);
+ cli_req_append_ascii4(req, parms->in.pattern, STR_TERMINATE);
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+ return req;
+}
+
+/*
+ delete a file - sync interface
+*/
+NTSTATUS smb_raw_unlink(struct cli_tree *tree,
+ struct smb_unlink *parms)
+{
+ struct cli_request *req = smb_raw_unlink_send(tree, parms);
+ return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ create a directory using TRANSACT2_MKDIR - async interface
+****************************************************************************/
+static struct cli_request *smb_raw_t2mkdir_send(struct cli_tree *tree,
+ union smb_mkdir *parms)
+{
+ struct smb_trans2 t2;
+ uint16 setup = TRANSACT2_MKDIR;
+ TALLOC_CTX *mem_ctx;
+ struct cli_request *req;
+ uint16 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 = 0;
+ 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 */
+
+ cli_blob_append_string(tree->session, mem_ctx,
+ &t2.in.params, parms->t2mkdir.in.path, 0);
+
+ ea_put_list(t2.in.data.data, parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas);
+
+ req = smb_raw_trans2_send(tree, &t2);
+
+ talloc_destroy(mem_ctx);
+
+ return req;
+}
+
+/****************************************************************************
+ Create a directory - async interface
+****************************************************************************/
+struct cli_request *smb_raw_mkdir_send(struct cli_tree *tree,
+ union smb_mkdir *parms)
+{
+ struct cli_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);
+
+ cli_req_append_ascii4(req, parms->mkdir.in.path, STR_TERMINATE);
+
+ if (!cli_request_send(req)) {
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Create a directory - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_mkdir(struct cli_tree *tree,
+ union smb_mkdir *parms)
+{
+ struct cli_request *req = smb_raw_mkdir_send(tree, parms);
+ return cli_request_simple_recv(req);
+}
+
+/****************************************************************************
+ Remove a directory - async interface
+****************************************************************************/
+struct cli_request *smb_raw_rmdir_send(struct cli_tree *tree,
+ struct smb_rmdir *parms)
+{
+ struct cli_request *req;
+
+ SETUP_REQUEST(SMBrmdir, 0, 0);
+
+ cli_req_append_ascii4(req, parms->in.path, STR_TERMINATE);
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Remove a directory - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_rmdir(struct cli_tree *tree,
+ struct smb_rmdir *parms)
+{
+ struct cli_request *req = smb_raw_rmdir_send(tree, parms);
+ return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Open a file using TRANSACT2_OPEN - async send
+****************************************************************************/
+static struct cli_request *smb_raw_t2open_send(struct cli_tree *tree,
+ union smb_open *parms)
+{
+ struct smb_trans2 t2;
+ uint16 setup = TRANSACT2_OPEN;
+ TALLOC_CTX *mem_ctx = talloc_init("smb_raw_t2open");
+ struct cli_request *req;
+ uint16 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), 0); /* reserved */
+ SSVAL(t2.in.params.data, VWV(3), parms->t2open.in.file_attrs);
+ put_dos_date(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);
+
+ cli_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_destroy(mem_ctx);
+
+ return req;
+}
+
+
+/****************************************************************************
+ Open a file using TRANSACT2_OPEN - async recv
+****************************************************************************/
+static NTSTATUS smb_raw_t2open_recv(struct cli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms)
+{
+ 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.fnum = SVAL(t2.out.params.data, VWV(0));
+ parms->t2open.out.attrib = SVAL(t2.out.params.data, VWV(1));
+ parms->t2open.out.write_time = make_unix_date3(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.unknown = SVAL(t2.out.params.data, VWV(10));
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Open a file - async send
+****************************************************************************/
+struct cli_request *smb_raw_open_send(struct cli_tree *tree, union smb_open *parms)
+{
+ int len;
+ struct cli_request *req = NULL;
+
+ switch (parms->open.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->open.in.flags);
+ SSVAL(req->out.vwv, VWV(1), parms->open.in.search_attrs);
+ cli_req_append_ascii4(req, parms->open.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);
+ put_dos_date3(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 */
+ cli_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);
+ put_dos_date3(req->out.vwv, VWV(1), parms->mknew.in.write_time);
+ cli_req_append_ascii4(req, parms->mknew.in.fname, STR_TERMINATE);
+ break;
+
+ case RAW_OPEN_CTEMP:
+ SETUP_REQUEST(SMBctemp, 3, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->ctemp.in.attrib);
+ put_dos_date3(req->out.vwv, VWV(1), parms->ctemp.in.write_time);
+ cli_req_append_ascii4(req, parms->ctemp.in.directory, STR_TERMINATE);
+ break;
+
+ case RAW_OPEN_SPLOPEN:
+ SETUP_REQUEST(SMBsplopen, 2, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->splopen.in.setup_length);
+ SSVAL(req->out.vwv, VWV(1), parms->splopen.in.mode);
+ break;
+
+ case RAW_OPEN_NTCREATEX:
+ SETUP_REQUEST(SMBntcreateX, 24, 0);
+ SSVAL(req->out.vwv, VWV(0),SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1),0);
+ SCVAL(req->out.vwv, VWV(2),0); /* padding */
+ SIVAL(req->out.vwv, 7, parms->ntcreatex.in.flags);
+ SIVAL(req->out.vwv, 11, parms->ntcreatex.in.root_fid);
+ SIVAL(req->out.vwv, 15, parms->ntcreatex.in.access_mask);
+ SBVAL(req->out.vwv, 19, parms->ntcreatex.in.alloc_size);
+ SIVAL(req->out.vwv, 27, parms->ntcreatex.in.file_attr);
+ SIVAL(req->out.vwv, 31, parms->ntcreatex.in.share_access);
+ SIVAL(req->out.vwv, 35, parms->ntcreatex.in.open_disposition);
+ SIVAL(req->out.vwv, 39, parms->ntcreatex.in.create_options);
+ SIVAL(req->out.vwv, 43, parms->ntcreatex.in.impersonation);
+ SCVAL(req->out.vwv, 47, parms->ntcreatex.in.security_flags);
+
+ cli_req_append_string_len(req, parms->ntcreatex.in.fname, STR_TERMINATE, &len);
+ SSVAL(req->out.vwv, 5, len);
+ break;
+ }
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Open a file - async recv
+****************************************************************************/
+NTSTATUS smb_raw_open_recv(struct cli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms)
+{
+ if (!cli_request_receive(req) ||
+ cli_request_is_error(req)) {
+ goto failed;
+ }
+
+ switch (parms->open.level) {
+ case RAW_OPEN_T2OPEN:
+ return smb_raw_t2open_recv(req, mem_ctx, parms);
+
+ case RAW_OPEN_OPEN:
+ CLI_CHECK_WCT(req, 7);
+ parms->open.out.fnum = SVAL(req->in.vwv, VWV(0));
+ parms->open.out.attrib = SVAL(req->in.vwv, VWV(1));
+ parms->open.out.write_time = make_unix_date3(req->in.vwv + VWV(2));
+ parms->open.out.size = IVAL(req->in.vwv, VWV(4));
+ parms->open.out.rmode = SVAL(req->in.vwv, VWV(6));
+ break;
+
+ case RAW_OPEN_OPENX:
+ CLI_CHECK_MIN_WCT(req, 15);
+ parms->openx.out.fnum = SVAL(req->in.vwv, VWV(2));
+ parms->openx.out.attrib = SVAL(req->in.vwv, VWV(3));
+ parms->openx.out.write_time = make_unix_date3(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:
+ CLI_CHECK_WCT(req, 1);
+ parms->mknew.out.fnum = SVAL(req->in.vwv, VWV(0));
+ break;
+
+ case RAW_OPEN_CTEMP:
+ CLI_CHECK_WCT(req, 1);
+ parms->ctemp.out.fnum = SVAL(req->in.vwv, VWV(0));
+ cli_req_pull_string(req, mem_ctx, &parms->ctemp.out.name, req->in.data, -1, STR_TERMINATE | STR_ASCII);
+ break;
+
+ case RAW_OPEN_SPLOPEN:
+ CLI_CHECK_WCT(req, 1);
+ parms->splopen.out.fnum = SVAL(req->in.vwv, VWV(0));
+ break;
+
+ case RAW_OPEN_NTCREATEX:
+ CLI_CHECK_MIN_WCT(req, 34);
+ parms->ntcreatex.out.oplock_level = CVAL(req->in.vwv, 4);
+ parms->ntcreatex.out.fnum = SVAL(req->in.vwv, 5);
+ parms->ntcreatex.out.create_action = IVAL(req->in.vwv, 7);
+ parms->ntcreatex.out.create_time = cli_pull_nttime(req->in.vwv, 11);
+ parms->ntcreatex.out.access_time = cli_pull_nttime(req->in.vwv, 19);
+ parms->ntcreatex.out.write_time = cli_pull_nttime(req->in.vwv, 27);
+ parms->ntcreatex.out.change_time = cli_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;
+ }
+
+failed:
+ return cli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Open a file - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_open(struct cli_tree *tree, TALLOC_CTX *mem_ctx, union smb_open *parms)
+{
+ struct cli_request *req = smb_raw_open_send(tree, parms);
+ return smb_raw_open_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ Close a file - async send
+****************************************************************************/
+struct cli_request *smb_raw_close_send(struct cli_tree *tree, union smb_close *parms)
+{
+ struct cli_request *req;
+
+ switch (parms->generic.level) {
+ case RAW_CLOSE_GENERIC:
+ return NULL;
+
+ case RAW_CLOSE_CLOSE:
+ SETUP_REQUEST(SMBclose, 3, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->close.in.fnum);
+ put_dos_date3(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.fnum);
+ SIVAL(req->out.vwv, VWV(1), 0); /* reserved */
+ break;
+ }
+
+ if (!req) return NULL;
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ Close a file - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_close(struct cli_tree *tree, union smb_close *parms)
+{
+ struct cli_request *req = smb_raw_close_send(tree, parms);
+ return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Locking calls - async interface
+****************************************************************************/
+struct cli_request *smb_raw_lock_send(struct cli_tree *tree, union smb_lock *parms)
+{
+ struct cli_request *req;
+
+ switch (parms->generic.level) {
+ case RAW_LOCK_GENERIC:
+ return NULL;
+
+ case RAW_LOCK_LOCK:
+ SETUP_REQUEST(SMBlock, 5, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->lock.in.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.fnum);
+ SIVAL(req->out.vwv, VWV(1), parms->unlock.in.count);
+ SIVAL(req->out.vwv, VWV(3), parms->unlock.in.offset);
+ break;
+
+ case RAW_LOCK_LOCKX: {
+ struct smb_lock_entry *lockp;
+ uint_t lck_size = (parms->lockx.in.mode & LOCKING_ANDX_LARGE_FILES)? 20 : 10;
+ uint_t lock_count = parms->lockx.in.ulock_cnt + parms->lockx.in.lock_cnt;
+ int i;
+
+ SETUP_REQUEST(SMBlockingX, 8, lck_size * lock_count);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->lockx.in.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++) {
+ char *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);
+ }
+ }
+ }
+ }
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Locking calls - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_lock(struct cli_tree *tree, union smb_lock *parms)
+{
+ struct cli_request *req = smb_raw_lock_send(tree, parms);
+ return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Check for existence of a dir - async send
+****************************************************************************/
+struct cli_request *smb_raw_chkpath_send(struct cli_tree *tree, struct smb_chkpath *parms)
+{
+ struct cli_request *req;
+
+ SETUP_REQUEST(SMBchkpth, 0, 0);
+
+ cli_req_append_ascii4(req, parms->in.path, STR_TERMINATE);
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Check for existence of a dir - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_chkpath(struct cli_tree *tree, struct smb_chkpath *parms)
+{
+ struct cli_request *req = smb_raw_chkpath_send(tree, parms);
+ return cli_request_simple_recv(req);
+}
+
+
+
+
+/****************************************************************************
+ flush a file - async send
+ a flush to fnum 0xFFFF will flush all files
+****************************************************************************/
+struct cli_request *smb_raw_flush_send(struct cli_tree *tree, struct smb_flush *parms)
+{
+ struct cli_request *req;
+
+ SETUP_REQUEST(SMBflush, 1, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->in.fnum);
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ flush a file - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_flush(struct cli_tree *tree, struct smb_flush *parms)
+{
+ struct cli_request *req = smb_raw_flush_send(tree, parms);
+ return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ seek a file - async send
+****************************************************************************/
+struct cli_request *smb_raw_seek_send(struct cli_tree *tree,
+ struct smb_seek *parms)
+{
+ struct cli_request *req;
+
+ SETUP_REQUEST(SMBlseek, 4, 0);
+
+ SSVAL(req->out.vwv, VWV(0), parms->in.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->in.mode);
+ SIVALS(req->out.vwv, VWV(2), parms->in.offset);
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+ return req;
+}
+
+/****************************************************************************
+ seek a file - async receive
+****************************************************************************/
+NTSTATUS smb_raw_seek_recv(struct cli_request *req,
+ struct smb_seek *parms)
+{
+ if (!cli_request_receive(req) ||
+ cli_request_is_error(req)) {
+ return cli_request_destroy(req);
+ }
+
+ CLI_CHECK_WCT(req, 2);
+ parms->out.offset = IVAL(req->in.vwv, VWV(0));
+
+failed:
+ return cli_request_destroy(req);
+}
+
+/*
+ seek a file - sync interface
+*/
+NTSTATUS smb_raw_seek(struct cli_tree *tree,
+ struct smb_seek *parms)
+{
+ struct cli_request *req = smb_raw_seek_send(tree, parms);
+ return smb_raw_seek_recv(req, parms);
+}
diff --git a/source4/libcli/raw/rawfileinfo.c b/source4/libcli/raw/rawfileinfo.c
new file mode 100644
index 0000000000..f685cef9c3
--- /dev/null
+++ b/source4/libcli/raw/rawfileinfo.c
@@ -0,0 +1,527 @@
+/*
+ Unix SMB/CIFS implementation.
+ client trans2 operations
+ Copyright (C) James Myers 2003
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.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", \
+ 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", \
+ blob->length, parms->generic.level, (size))); \
+ return NT_STATUS_INFO_LENGTH_MISMATCH; \
+}
+
+/****************************************************************************
+ Handle qfileinfo/qpathinfo trans2 backend.
+****************************************************************************/
+static NTSTATUS smb_raw_info_backend(struct cli_session *session,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms,
+ DATA_BLOB *blob)
+{
+ uint_t len, ofs;
+
+ switch (parms->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ case RAW_FILEINFO_GETATTR:
+ case RAW_FILEINFO_GETATTRE:
+ /* not handled here */
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_FILEINFO_STANDARD:
+ FINFO_CHECK_SIZE(22);
+ parms->standard.out.create_time = make_unix_date2(blob->data + 0);
+ parms->standard.out.access_time = make_unix_date2(blob->data + 4);
+ parms->standard.out.write_time = make_unix_date2(blob->data + 8);
+ parms->standard.out.size = IVAL(blob->data, 12);
+ parms->standard.out.alloc_size = IVAL(blob->data, 16);
+ parms->standard.out.attrib = SVAL(blob->data, 20);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_SIZE:
+ FINFO_CHECK_SIZE(26);
+ parms->ea_size.out.create_time = make_unix_date2(blob->data + 0);
+ parms->ea_size.out.access_time = make_unix_date2(blob->data + 4);
+ parms->ea_size.out.write_time = make_unix_date2(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_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:
+ /* 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 = cli_pull_nttime(blob->data, 0);
+ parms->basic_info.out.access_time = cli_pull_nttime(blob->data, 8);
+ parms->basic_info.out.write_time = cli_pull_nttime(blob->data, 16);
+ parms->basic_info.out.change_time = cli_pull_nttime(blob->data, 24);
+ parms->basic_info.out.attrib = IVAL(blob->data, 32);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STANDARD_INFO:
+ 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_INFO:
+ 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_INFO:
+ case RAW_FILEINFO_NAME_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(4);
+ cli_blob_pull_string(session, mem_ctx, blob,
+ &parms->name_info.out.fname, 0, 4, STR_UNICODE);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALL_INFO:
+ case RAW_FILEINFO_ALL_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(72);
+ parms->all_info.out.create_time = cli_pull_nttime(blob->data, 0);
+ parms->all_info.out.access_time = cli_pull_nttime(blob->data, 8);
+ parms->all_info.out.write_time = cli_pull_nttime(blob->data, 16);
+ parms->all_info.out.change_time = cli_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);
+ parms->all_info.out.ea_size = IVAL(blob->data, 64);
+ cli_blob_pull_string(session, mem_ctx, blob,
+ &parms->all_info.out.fname, 68, 72, STR_UNICODE);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(4);
+ cli_blob_pull_string(session, mem_ctx, blob,
+ &parms->alt_name_info.out.fname, 0, 4, STR_UNICODE);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(0);
+ ofs = 0;
+ parms->stream_info.out.num_streams = 0;
+ parms->stream_info.out.streams = NULL;
+
+ while (blob->length - ofs >= 24) {
+ uint_t n = parms->stream_info.out.num_streams;
+ parms->stream_info.out.streams =
+ talloc_realloc(mem_ctx,parms->stream_info.out.streams,
+ (n+1) * sizeof(parms->stream_info.out.streams[0]));
+ if (!parms->stream_info.out.streams) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ parms->stream_info.out.streams[n].size = BVAL(blob->data, ofs + 8);
+ parms->stream_info.out.streams[n].alloc_size = BVAL(blob->data, ofs + 16);
+ cli_blob_pull_string(session, mem_ctx, blob,
+ &parms->stream_info.out.streams[n].stream_name,
+ ofs+4, ofs+24, STR_UNICODE);
+ parms->stream_info.out.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;
+
+ case RAW_FILEINFO_INTERNAL_INFORMATION:
+ FINFO_CHECK_SIZE(8);
+ parms->internal_information.out.device = IVAL(blob->data, 0);
+ parms->internal_information.out.inode = IVAL(blob->data, 4);
+ 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_INFO:
+ 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_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 = cli_pull_nttime(blob->data, 16);
+ parms->unix_basic_info.out.access_time = cli_pull_nttime(blob->data, 24);
+ parms->unix_basic_info.out.change_time = cli_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_LINK:
+ FINFO_CHECK_MIN_SIZE(0);
+ cli_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:
+ FINFO_CHECK_SIZE(56);
+ parms->network_open_information.out.create_time = cli_pull_nttime(blob->data, 0);
+ parms->network_open_information.out.access_time = cli_pull_nttime(blob->data, 8);
+ parms->network_open_information.out.write_time = cli_pull_nttime(blob->data, 16);
+ parms->network_open_information.out.change_time = cli_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;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/****************************************************************************
+ Very raw query file info - returns param/data blobs - (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_fileinfo_blob_send(struct cli_tree *tree,
+ uint16 fnum, uint16 info_level)
+{
+ struct smb_trans2 tp;
+ uint16 setup = TRANSACT2_QFILEINFO;
+ struct cli_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_blob(NULL, 0);
+ 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_destroy(mem_ctx);
+ return NULL;
+ }
+
+ SIVAL(tp.in.params.data, 0, fnum);
+ SSVAL(tp.in.params.data, 2, info_level);
+
+ req = smb_raw_trans2_send(tree, &tp);
+
+ talloc_destroy(mem_ctx);
+
+ return req;
+}
+
+
+/****************************************************************************
+ Very raw query file info - returns param/data blobs - (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_fileinfo_blob_recv(struct cli_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 cli_request *smb_raw_pathinfo_blob_send(struct cli_tree *tree,
+ const char *fname,
+ uint16 info_level)
+{
+ struct smb_trans2 tp;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ struct cli_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_blob(NULL, 0);
+ 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_destroy(mem_ctx);
+ return NULL;
+ }
+
+ SSVAL(tp.in.params.data, 0, info_level);
+ SIVAL(tp.in.params.data, 2, 0);
+ cli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
+ fname, STR_TERMINATE);
+
+ req = smb_raw_trans2_send(tree, &tp);
+
+ talloc_destroy(mem_ctx);
+
+ return req;
+}
+
+/****************************************************************************
+ send a SMBgetatr (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_getattr_send(struct cli_tree *tree,
+ union smb_fileinfo *parms)
+{
+ struct cli_request *req;
+
+ req = cli_request_setup(tree, SMBgetatr, 0, 0);
+ if (!req) return NULL;
+
+ cli_req_append_ascii4(req, parms->getattr.in.fname, STR_TERMINATE);
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ send a SMBgetatr (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_getattr_recv(struct cli_request *req,
+ union smb_fileinfo *parms)
+{
+ if (!cli_request_receive(req) ||
+ cli_request_is_error(req)) {
+ return cli_request_destroy(req);
+ }
+
+ CLI_CHECK_WCT(req, 10);
+ parms->getattr.out.attrib = SVAL(req->in.vwv, VWV(0));
+ parms->getattr.out.write_time = make_unix_date3(req->in.vwv + VWV(1));
+ parms->getattr.out.size = IVAL(req->in.vwv, VWV(3));
+
+failed:
+ return cli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Handle SMBgetattrE (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_getattrE_send(struct cli_tree *tree,
+ union smb_fileinfo *parms)
+{
+ struct cli_request *req;
+
+ req = cli_request_setup(tree, SMBgetattrE, 1, 0);
+ if (!req) return NULL;
+
+ SSVAL(req->out.vwv, VWV(0), parms->getattre.in.fnum);
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Handle SMBgetattrE (async send)
+****************************************************************************/
+static NTSTATUS smb_raw_getattrE_recv(struct cli_request *req,
+ union smb_fileinfo *parms)
+{
+ if (!cli_request_receive(req) ||
+ cli_request_is_error(req)) {
+ return cli_request_destroy(req);
+ }
+
+ CLI_CHECK_WCT(req, 11);
+ parms->getattre.out.create_time = make_unix_date2(req->in.vwv + VWV(0));
+ parms->getattre.out.access_time = make_unix_date2(req->in.vwv + VWV(2));
+ parms->getattre.out.write_time = make_unix_date2(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 cli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Query file info (async send)
+****************************************************************************/
+struct cli_request *smb_raw_fileinfo_send(struct cli_tree *tree,
+ union smb_fileinfo *parms)
+{
+ /* 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_GENERIC) {
+ return NULL;
+ }
+
+ return smb_raw_fileinfo_blob_send(tree,
+ parms->generic.in.fnum,
+ parms->generic.level);
+}
+
+/****************************************************************************
+ Query file info (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_fileinfo_recv(struct cli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+ struct cli_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_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)
+****************************************************************************/
+NTSTATUS smb_raw_fileinfo(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms)
+{
+ struct cli_request *req = smb_raw_fileinfo_send(tree, parms);
+ return smb_raw_fileinfo_recv(req, mem_ctx, parms);
+}
+
+/****************************************************************************
+ Query path info (async send)
+****************************************************************************/
+struct cli_request *smb_raw_pathinfo_send(struct cli_tree *tree,
+ union smb_fileinfo *parms)
+{
+ if (parms->generic.level == RAW_FILEINFO_GETATTR) {
+ return smb_raw_getattr_send(tree, parms);
+ }
+ if (parms->generic.level >= RAW_FILEINFO_GENERIC) {
+ return NULL;
+ }
+
+ return smb_raw_pathinfo_blob_send(tree, parms->generic.in.fname,
+ parms->generic.level);
+}
+
+/****************************************************************************
+ Query path info (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_pathinfo_recv(struct cli_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)
+****************************************************************************/
+NTSTATUS smb_raw_pathinfo(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms)
+{
+ struct cli_request *req = smb_raw_pathinfo_send(tree, parms);
+ return smb_raw_pathinfo_recv(req, mem_ctx, parms);
+}
diff --git a/source4/libcli/raw/rawfsinfo.c b/source4/libcli/raw/rawfsinfo.c
new file mode 100644
index 0000000000..362063bfc5
--- /dev/null
+++ b/source4/libcli/raw/rawfsinfo.c
@@ -0,0 +1,282 @@
+/*
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Query FS Info - SMBdskattr call (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_dskattr_send(struct cli_tree *tree,
+ union smb_fsinfo *fsinfo)
+{
+ struct cli_request *req;
+
+ req = cli_request_setup(tree, SMBdskattr, 0, 0);
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Query FS Info - SMBdskattr call (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_dskattr_recv(struct cli_request *req,
+ union smb_fsinfo *fsinfo)
+{
+ if (!cli_request_receive(req) ||
+ cli_request_is_error(req)) {
+ goto failed;
+ }
+
+ CLI_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 cli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ RAW_QFS_ trans2 interface via blobs (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_qfsinfo_send(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ uint16 info_level)
+{
+ struct smb_trans2 tp;
+ uint16 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 = 0x1000; /* plenty for all possible QFS levels */
+ 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 cli_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", \
+ 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", \
+ blob.length, fsinfo->generic.level, (size))); \
+ status = NT_STATUS_INFO_LENGTH_MISMATCH; \
+ goto failed; \
+}
+
+
+/****************************************************************************
+ Query FSInfo raw interface (async send)
+****************************************************************************/
+struct cli_request *smb_raw_fsinfo_send(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *fsinfo)
+{
+ uint16 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)fsinfo->generic.level;
+
+ return smb_raw_qfsinfo_send(tree, mem_ctx, info_level);
+}
+
+
+/****************************************************************************
+ Query FSInfo raw interface (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_fsinfo_recv(struct cli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *fsinfo)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+ int i;
+ struct cli_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);
+ cli_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:
+ QFS_CHECK_MIN_SIZE(18);
+ fsinfo->volume_info.out.create_time = cli_pull_nttime(blob.data, 0);
+ fsinfo->volume_info.out.serial_number = IVAL(blob.data, 8);
+ cli_blob_pull_string(session, mem_ctx, &blob,
+ &fsinfo->volume_info.out.volume_name,
+ 12, 18, STR_UNICODE);
+ break;
+
+ case RAW_QFS_SIZE_INFO:
+ 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_INFO:
+ 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_INFO:
+ 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);
+ cli_blob_pull_string(session, mem_ctx, &blob,
+ &fsinfo->attribute_info.out.fs_type,
+ 8, 12, STR_UNICODE);
+ break;
+
+ 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:
+ QFS_CHECK_SIZE(48);
+ fsinfo->quota_information.out.unknown[0] = BVAL(blob.data, 0);
+ fsinfo->quota_information.out.unknown[1] = BVAL(blob.data, 8);
+ fsinfo->quota_information.out.unknown[2] = BVAL(blob.data, 16);
+ fsinfo->quota_information.out.quota_soft = BVAL(blob.data, 24);
+ fsinfo->quota_information.out.quota_hard = BVAL(blob.data, 32);
+ fsinfo->quota_information.out.quota_flags = BVAL(blob.data, 40);
+ break;
+
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ QFS_CHECK_SIZE(32);
+ fsinfo->full_size_information.out.total_alloc_units = BVAL(blob.data, 0);
+ fsinfo->full_size_information.out.call_avail_alloc_units = BVAL(blob.data, 8);
+ fsinfo->full_size_information.out.actual_avail_alloc_units = BVAL(blob.data, 16);
+ fsinfo->full_size_information.out.sectors_per_unit = IVAL(blob.data, 24);
+ fsinfo->full_size_information.out.bytes_per_sector = IVAL(blob.data, 28);
+ break;
+
+ case RAW_QFS_OBJECTID_INFORMATION:
+ QFS_CHECK_SIZE(64);
+ memcpy(fsinfo->objectid_information.out.guid.info, blob.data, GUID_SIZE);
+ for (i=0;i<6;i++) {
+ fsinfo->objectid_information.out.unknown[i] = BVAL(blob.data, 16 + i*8);
+ }
+ break;
+ }
+
+failed:
+ return status;
+}
+
+/****************************************************************************
+ Query FSInfo raw interface (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_fsinfo(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *fsinfo)
+{
+ struct cli_request *req = smb_raw_fsinfo_send(tree, mem_ctx, fsinfo);
+ return smb_raw_fsinfo_recv(req, mem_ctx, fsinfo);
+}
diff --git a/source4/libcli/raw/rawioctl.c b/source4/libcli/raw/rawioctl.c
new file mode 100644
index 0000000000..506bddd497
--- /dev/null
+++ b/source4/libcli/raw/rawioctl.c
@@ -0,0 +1,118 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define SETUP_REQUEST(cmd, wct, buflen) do { \
+ req = cli_request_setup(tree, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+/*
+ send a raw ioctl - async send
+*/
+struct cli_request *smb_raw_ioctl_send(struct cli_tree *tree, struct smb_ioctl *parms)
+{
+ struct cli_request *req;
+
+ SETUP_REQUEST(SMBioctl, 3, 0);
+
+ SSVAL(req->out.vwv, VWV(0), parms->in.fnum);
+ SIVAL(req->out.vwv, VWV(1), parms->in.request);
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/*
+ send a raw ioctl - async recv
+*/
+NTSTATUS smb_raw_ioctl_recv(struct cli_request *req, TALLOC_CTX *mem_ctx, struct smb_ioctl *parms)
+{
+ if (!cli_request_receive(req) ||
+ cli_request_is_error(req)) {
+ return cli_request_destroy(req);
+ }
+
+ parms->out.blob = cli_req_pull_blob(req, mem_ctx, req->in.data, -1);
+ return cli_request_destroy(req);
+}
+
+/*
+ send a raw ioctl - sync interface
+*/
+NTSTATUS smb_raw_ioctl(struct cli_tree *tree, TALLOC_CTX *mem_ctx, struct smb_ioctl *parms)
+{
+ struct cli_request *req = smb_raw_ioctl_send(tree, parms);
+ return smb_raw_ioctl_recv(req, mem_ctx, parms);
+}
+
+
+
+
+/****************************************************************************
+NT ioctl (async send)
+****************************************************************************/
+struct cli_request *smb_raw_ntioctl_send(struct cli_tree *tree,
+ struct smb_ntioctl *parms)
+{
+ struct smb_nttrans nt;
+ uint16 setup[4];
+
+ nt.in.max_setup = 0;
+ nt.in.max_param = 0;
+ nt.in.max_data = 0;
+ nt.in.setup_count = 4;
+ nt.in.setup = setup;
+ SIVAL(setup, 0, parms->in.function);
+ SSVAL(setup, 4, parms->in.fnum);
+ SCVAL(setup, 6, parms->in.fsctl);
+ SCVAL(setup, 7, parms->in.filter);
+ nt.in.function = NT_TRANSACT_IOCTL;
+ nt.in.params = data_blob(NULL, 0);
+ nt.in.data = data_blob(NULL, 0);
+
+ return smb_raw_nttrans_send(tree, &nt);
+}
+
+/****************************************************************************
+NT ioctl (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_ntioctl_recv(struct cli_request *req,
+ struct smb_ntioctl *parms)
+{
+ struct smb_nttrans nt;
+
+ return smb_raw_nttrans_recv(req, req->mem_ctx, &nt);
+}
+
+/****************************************************************************
+NT ioctl (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_ntioctl(struct cli_tree *tree,
+ struct smb_ntioctl *parms)
+{
+ struct cli_request *req = smb_raw_ntioctl_send(tree, parms);
+ return smb_raw_ntioctl_recv(req, parms);
+}
diff --git a/source4/libcli/raw/rawnegotiate.c b/source4/libcli/raw/rawnegotiate.c
new file mode 100644
index 0000000000..78b2e00706
--- /dev/null
+++ b/source4/libcli/raw/rawnegotiate.c
@@ -0,0 +1,157 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client negotiate context management functions
+ Copyright (C) Andrew Tridgell 1994-1998
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static const struct {
+ int prot;
+ const char *name;
+} prots[] = {
+ {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"},
+ {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
+ {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
+ {PROTOCOL_LANMAN1,"LANMAN1.0"},
+ {PROTOCOL_LANMAN1,"Windows for Workgroups 3.1a"},
+ {PROTOCOL_LANMAN2,"LM1.2X002"},
+ {PROTOCOL_LANMAN2,"DOS LANMAN2.1"},
+ {PROTOCOL_LANMAN2,"Samba"},
+ {PROTOCOL_NT1,"NT LANMAN 1.0"},
+ {PROTOCOL_NT1,"NT LM 0.12"},
+};
+
+/****************************************************************************
+ Send a negprot command.
+****************************************************************************/
+struct cli_request *smb_negprot_send(struct cli_transport *transport, int maxprotocol)
+{
+ struct cli_request *req;
+ int i;
+
+ req = cli_request_setup_transport(transport, SMBnegprot, 0, 0);
+ if (!req) {
+ return NULL;
+ }
+
+ /* setup the protocol strings */
+ for (i=0; i < ARRAY_SIZE(prots) && prots[i].prot <= maxprotocol; i++) {
+ cli_req_append_bytes(req, "\2", 1);
+ cli_req_append_string(req, prots[i].name, STR_TERMINATE | STR_ASCII);
+ }
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Send a negprot command.
+****************************************************************************/
+NTSTATUS smb_raw_negotiate(struct cli_transport *transport)
+{
+ struct cli_request *req;
+ int protocol;
+
+ req = smb_negprot_send(transport, PROTOCOL_NT1);
+ if (!req) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!cli_request_receive(req) ||
+ cli_request_is_error(req)) {
+ return cli_request_destroy(req);
+ }
+
+ CLI_CHECK_MIN_WCT(req, 1);
+
+ protocol = SVALS(req->in.vwv, VWV(0));
+
+ if (protocol >= ARRAY_SIZE(prots) || protocol < 0) {
+ req->status = NT_STATUS_UNSUCCESSFUL;
+ return cli_request_destroy(req);
+ }
+
+ transport->negotiate.protocol = prots[protocol].prot;
+
+ if (transport->negotiate.protocol >= PROTOCOL_NT1) {
+ NTTIME ntt;
+
+ /* NT protocol */
+ CLI_CHECK_WCT(req, 17);
+ transport->negotiate.sec_mode = CVAL(req->in.vwv,VWV(1));
+ transport->negotiate.max_mux = SVAL(req->in.vwv,VWV(1)+1);
+ transport->negotiate.max_xmit = IVAL(req->in.vwv,VWV(3)+1);
+ transport->negotiate.sesskey = IVAL(req->in.vwv,VWV(7)+1);
+ transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(15)+1) * 60;
+
+ /* this time arrives in real GMT */
+ ntt = cli_pull_nttime(req->in.vwv, VWV(11)+1);
+ transport->negotiate.server_time = nt_time_to_unix(&ntt);
+ transport->negotiate.capabilities = IVAL(req->in.vwv,VWV(9)+1);
+
+ transport->negotiate.secblob = cli_req_pull_blob(req, transport->mem_ctx, req->in.data, req->in.data_size);
+ if (transport->negotiate.capabilities & CAP_RAW_MODE) {
+ transport->negotiate.readbraw_supported = True;
+ transport->negotiate.writebraw_supported = True;
+ }
+
+ /* work out if they sent us a workgroup */
+ if ((transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) &&
+ req->in.data_size > 16) {
+ cli_req_pull_string(req, transport->mem_ctx, &transport->negotiate.server_domain,
+ req->in.data+16,
+ req->in.data_size-16, STR_UNICODE|STR_NOALIGN);
+ }
+ } else if (transport->negotiate.protocol >= PROTOCOL_LANMAN1) {
+ CLI_CHECK_WCT(req, 13);
+ transport->negotiate.sec_mode = SVAL(req->in.vwv,VWV(1));
+ transport->negotiate.max_xmit = SVAL(req->in.vwv,VWV(2));
+ transport->negotiate.sesskey = IVAL(req->in.vwv,VWV(6));
+ transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(10)) * 60;
+
+ /* this time is converted to GMT by make_unix_date */
+ transport->negotiate.server_time = make_unix_date(req->in.vwv+VWV(8));
+ if ((SVAL(req->in.vwv,VWV(5)) & 0x1)) {
+ transport->negotiate.readbraw_supported = 1;
+ }
+ if ((SVAL(req->in.vwv,VWV(5)) & 0x2)) {
+ transport->negotiate.writebraw_supported = 1;
+ }
+ transport->negotiate.secblob = cli_req_pull_blob(req, transport->mem_ctx,
+ req->in.data, req->in.data_size);
+ } else {
+ /* the old core protocol */
+ transport->negotiate.sec_mode = 0;
+ transport->negotiate.server_time = time(NULL);
+ transport->negotiate.max_xmit = ~0;
+ transport->negotiate.server_zone = TimeDiff(time(NULL));
+ }
+
+ /* a way to force ascii SMB */
+ if (getenv("CLI_FORCE_ASCII")) {
+ transport->negotiate.capabilities &= ~CAP_UNICODE;
+ }
+
+failed:
+ return cli_request_destroy(req);
+}
diff --git a/source4/libcli/raw/rawnotify.c b/source4/libcli/raw/rawnotify.c
new file mode 100644
index 0000000000..7d635da0dc
--- /dev/null
+++ b/source4/libcli/raw/rawnotify.c
@@ -0,0 +1,116 @@
+/*
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+change notify (async send)
+****************************************************************************/
+struct cli_request *smb_raw_changenotify_send(struct cli_tree *tree, struct smb_notify *parms)
+{
+ struct smb_nttrans nt;
+ uint16 setup[4];
+
+ nt.in.max_setup = 0;
+ nt.in.max_param = parms->in.buffer_size;
+ nt.in.max_data = 0;
+ nt.in.setup_count = 4;
+ nt.in.setup = setup;
+ SIVAL(setup, 0, parms->in.completion_filter);
+ SSVAL(setup, 4, parms->in.fnum);
+ SSVAL(setup, 6, parms->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)
+****************************************************************************/
+NTSTATUS smb_raw_changenotify_recv(struct cli_request *req,
+ TALLOC_CTX *mem_ctx, struct smb_notify *parms)
+{
+ struct smb_nttrans nt;
+ NTSTATUS status;
+ uint32 ofs, i;
+ struct cli_session *session = req?req->session:NULL;
+
+ status = smb_raw_nttrans_recv(req, mem_ctx, &nt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ parms->out.changes = NULL;
+ parms->out.num_changes = 0;
+
+ /* count them */
+ for (ofs=0; nt.out.params.length - ofs > 12; ) {
+ uint32 next = IVAL(nt.out.params.data, ofs);
+ parms->out.num_changes++;
+ if (next == 0 ||
+ ofs + next >= nt.out.params.length) break;
+ ofs += next;
+ }
+
+ /* allocate array */
+ parms->out.changes = talloc(mem_ctx, sizeof(parms->out.changes[0]) *
+ parms->out.num_changes);
+ if (!parms->out.changes) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=ofs=0; i<parms->out.num_changes; i++) {
+ parms->out.changes[i].action = IVAL(nt.out.params.data, ofs+4);
+ cli_blob_pull_string(session, mem_ctx, &nt.out.params,
+ &parms->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 cli_request *oldreq)
+{
+ struct cli_request *req;
+
+ req = cli_request_setup_transport(oldreq->transport, SMBntcancel, 0, 0);
+
+ SSVAL(req->out.hdr, HDR_MID, SVAL(oldreq->out.hdr, HDR_MID));
+ SSVAL(req->out.hdr, HDR_PID, SVAL(oldreq->out.hdr, HDR_PID));
+ SSVAL(req->out.hdr, HDR_TID, SVAL(oldreq->out.hdr, HDR_TID));
+ SSVAL(req->out.hdr, HDR_UID, SVAL(oldreq->out.hdr, HDR_UID));
+
+ /* this request does not expect a reply, so tell the signing
+ subsystem not to allocate an id for a reply */
+ req->one_way_request = 1;
+
+ cli_request_send(req);
+
+ return cli_request_destroy(req);
+}
diff --git a/source4/libcli/raw/rawreadwrite.c b/source4/libcli/raw/rawreadwrite.c
new file mode 100644
index 0000000000..84c7e3c00f
--- /dev/null
+++ b/source4/libcli/raw/rawreadwrite.c
@@ -0,0 +1,321 @@
+/*
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define SETUP_REQUEST(cmd, wct, buflen) do { \
+ req = cli_request_setup(tree, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+
+/****************************************************************************
+ low level read operation (async send)
+****************************************************************************/
+struct cli_request *smb_raw_read_send(struct cli_tree *tree, union smb_read *parms)
+{
+ BOOL bigoffset = False;
+ struct cli_request *req;
+
+ switch (parms->generic.level) {
+ case RAW_READ_GENERIC:
+ return NULL;
+
+ case RAW_READ_READBRAW:
+ if (parms->readbraw.in.offset >= 0x80000000) {
+ bigoffset = True;
+ }
+ SETUP_REQUEST(SMBreadbraw, bigoffset? 10:8, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->readbraw.in.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.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.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 (parms->readx.in.offset >= 0x80000000) {
+ bigoffset = True;
+ }
+ SETUP_REQUEST(SMBreadX, bigoffset ? 12 : 10, 0);
+ SSVAL(req->out.vwv, VWV(0), 0xFF);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->readx.in.fnum);
+ SIVAL(req->out.vwv, VWV(3), parms->readx.in.offset);
+ SSVAL(req->out.vwv, VWV(5), parms->readx.in.maxcnt);
+ SSVAL(req->out.vwv, VWV(6), parms->readx.in.mincnt);
+ SIVAL(req->out.vwv, VWV(7), 0); /* reserved */
+ SSVAL(req->out.vwv, VWV(9), parms->readx.in.remaining);
+ if (bigoffset) {
+ SIVAL(req->out.vwv, VWV(10),parms->readx.in.offset>>32);
+ }
+ break;
+ }
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ /* the transport layer needs to know that a readbraw is pending
+ and handle receives a little differently */
+ if (parms->generic.level == RAW_READ_READBRAW) {
+ tree->session->transport->readbraw_pending = 1;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ low level read operation (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_read_recv(struct cli_request *req, union smb_read *parms)
+{
+ if (!cli_request_receive(req) ||
+ cli_request_is_error(req)) {
+ goto failed;
+ }
+
+ switch (parms->generic.level) {
+ case RAW_READ_GENERIC:
+ /* handled in _send() */
+ break;
+
+ 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:
+ CLI_CHECK_WCT(req, 5);
+ parms->lockread.out.nread = SVAL(req->in.vwv, VWV(0));
+ if (parms->lockread.out.nread > parms->lockread.in.count ||
+ !cli_raw_pull_data(req, 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 */
+ CLI_CHECK_WCT(req, 5);
+ parms->read.out.nread = SVAL(req->in.vwv, VWV(0));
+ if (parms->read.out.nread > parms->read.in.count ||
+ !cli_raw_pull_data(req, 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 */
+ CLI_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));
+ if (parms->readx.out.nread > MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt) ||
+ !cli_raw_pull_data(req, 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;
+ }
+
+failed:
+ return cli_request_destroy(req);
+}
+
+/****************************************************************************
+ low level read operation (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_read(struct cli_tree *tree, union smb_read *parms)
+{
+ struct cli_request *req = smb_raw_read_send(tree, parms);
+ return smb_raw_read_recv(req, parms);
+}
+
+
+/****************************************************************************
+ raw write interface (async send)
+****************************************************************************/
+struct cli_request *smb_raw_write_send(struct cli_tree *tree, union smb_write *parms)
+{
+ BOOL bigoffset = False;
+ struct cli_request *req;
+
+ switch (parms->generic.level) {
+ case RAW_WRITE_GENERIC:
+ return NULL;
+
+ case RAW_WRITE_WRITEUNLOCK:
+ SETUP_REQUEST(SMBwriteunlock, 5, 3 + parms->writeunlock.in.count);
+ SSVAL(req->out.vwv, VWV(0), parms->writeunlock.in.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.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.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->writeclose.in.count);
+ SIVAL(req->out.vwv, VWV(2), parms->writeclose.in.offset);
+ put_dos_date3(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 (parms->writex.in.offset >= 0x80000000) {
+ bigoffset = True;
+ }
+ SETUP_REQUEST(SMBwriteX, bigoffset ? 14 : 12, parms->writex.in.count);
+ SSVAL(req->out.vwv, VWV(0), 0xFF);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->writex.in.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), 0); /* reserved */
+ 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.fnum);
+ if (parms->splwrite.in.count > 0) {
+ memcpy(req->out.data, parms->splwrite.in.data, parms->splwrite.in.count);
+ }
+ break;
+ }
+
+ if (!cli_request_send(req)) {
+cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ raw write interface (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_write_recv(struct cli_request *req, union smb_write *parms)
+{
+ if (!cli_request_receive(req) ||
+ cli_request_is_error(req)) {
+ goto failed;
+ }
+
+ switch (parms->generic.level) {
+ case RAW_WRITE_GENERIC:
+ break;
+ case RAW_WRITE_WRITEUNLOCK:
+ CLI_CHECK_WCT(req, 1);
+ parms->writeunlock.out.nwritten = SVAL(req->in.vwv, VWV(0));
+ break;
+ case RAW_WRITE_WRITE:
+ CLI_CHECK_WCT(req, 1);
+ parms->write.out.nwritten = SVAL(req->in.vwv, VWV(0));
+ break;
+ case RAW_WRITE_WRITECLOSE:
+ CLI_CHECK_WCT(req, 1);
+ parms->writeclose.out.nwritten = SVAL(req->in.vwv, VWV(0));
+ break;
+ case RAW_WRITE_WRITEX:
+ CLI_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;
+ }
+
+failed:
+ return cli_request_destroy(req);
+}
+
+/****************************************************************************
+ raw write interface (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_write(struct cli_tree *tree, union smb_write *parms)
+{
+ struct cli_request *req = smb_raw_write_send(tree, parms);
+ return smb_raw_write_recv(req, parms);
+}
diff --git a/source4/libcli/raw/rawrequest.c b/source4/libcli/raw/rawrequest.c
new file mode 100644
index 0000000000..9c2b2c7367
--- /dev/null
+++ b/source4/libcli/raw/rawrequest.c
@@ -0,0 +1,1019 @@
+/*
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ this file implements functions for manipulating the 'struct cli_request' structure in libsmb
+*/
+
+#include "includes.h"
+
+/* we over allocate the data buffer to prevent too many realloc calls */
+#define REQ_OVER_ALLOCATION 256
+
+/* assume that a character will not consume more than 3 bytes per char */
+#define MAX_BYTES_PER_CHAR 3
+
+/* destroy a request structure and return final status */
+NTSTATUS cli_request_destroy(struct cli_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;
+
+ /* remove it from the list of pending requests (a null op if
+ its not in the list) */
+ DLIST_REMOVE(req->transport->pending_requests, req);
+
+ /* ahh, its so nice to destroy a complex structure in such a
+ simple way! */
+ status = req->status;
+ talloc_destroy(req->mem_ctx);
+ return status;
+}
+
+
+/*
+ low-level function to setup a request buffer for a non-SMB packet
+ at the transport level
+*/
+struct cli_request *cli_request_setup_nonsmb(struct cli_transport *transport, uint_t size)
+{
+ struct cli_request *req;
+ TALLOC_CTX *mem_ctx;
+
+ /* each request gets its own talloc context. The request
+ structure itself is also allocated inside this context,
+ so we need to allocate it before we construct the request
+ */
+ mem_ctx = talloc_init("cli_request");
+ if (!mem_ctx) {
+ return NULL;
+ }
+
+ req = talloc(mem_ctx, sizeof(struct cli_request));
+ if (!req) {
+ return NULL;
+ }
+ ZERO_STRUCTP(req);
+
+ /* setup the request context */
+ req->mem_ctx = mem_ctx;
+ req->transport = transport;
+ req->session = NULL;
+ req->tree = NULL;
+ req->out.size = size;
+
+ /* over allocate by a small amount */
+ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
+
+ req->out.buffer = talloc(req->mem_ctx, req->out.allocated);
+ if (!req->out.buffer) {
+ return NULL;
+ }
+
+ SIVAL(req->out.buffer, 0, 0);
+
+ return req;
+}
+
+
+/*
+ setup a SMB packet at transport level
+*/
+struct cli_request *cli_request_setup_transport(struct cli_transport *transport,
+ uint8 command, unsigned wct, unsigned buflen)
+{
+ struct cli_request *req;
+
+ req = cli_request_setup_nonsmb(transport, NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen);
+
+ if (!req) return NULL;
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.vwv = req->out.hdr + HDR_VWV;
+ req->out.wct = wct;
+ req->out.data = req->out.vwv + VWV(wct) + 2;
+ req->out.data_size = buflen;
+ req->out.ptr = req->out.data;
+
+ SCVAL(req->out.hdr, HDR_WCT, wct);
+ SSVAL(req->out.vwv, VWV(wct), buflen);
+
+ memcpy(req->out.hdr, "\377SMB", 4);
+ SCVAL(req->out.hdr,HDR_COM,command);
+
+ SCVAL(req->out.hdr,HDR_FLG, FLAG_CASELESS_PATHNAMES);
+ SSVAL(req->out.hdr,HDR_FLG2, 0);
+
+ /* assign a mid */
+ req->mid = cli_transport_next_mid(transport);
+
+ /* copy the pid, uid and mid to the request */
+ SSVAL(req->out.hdr, HDR_PID, 0);
+ SSVAL(req->out.hdr, HDR_UID, 0);
+ SSVAL(req->out.hdr, HDR_MID, req->mid);
+ SSVAL(req->out.hdr, HDR_TID,0);
+ SSVAL(req->out.hdr, HDR_PIDHIGH,0);
+ SIVAL(req->out.hdr, HDR_RCLS, 0);
+ memset(req->out.hdr+HDR_SS_FIELD, 0, 10);
+
+ return req;
+}
+
+/*
+ setup a reply in req->out with the given word count and initial data
+ buffer size. the caller will then fill in the command words and
+ data before calling cli_request_send() to send the reply on its
+ way. This interface is used before a session is setup.
+*/
+struct cli_request *cli_request_setup_session(struct cli_session *session,
+ uint8 command, unsigned wct, unsigned buflen)
+{
+ struct cli_request *req;
+ uint16 flags2;
+ uint32 capabilities;
+
+ req = cli_request_setup_transport(session->transport, command, wct, buflen);
+
+ if (!req) return NULL;
+
+ req->session = session;
+
+ flags2 = FLAGS2_LONG_PATH_COMPONENTS;
+ capabilities = session->transport->negotiate.capabilities;
+
+ if (capabilities & CAP_UNICODE) {
+ flags2 |= FLAGS2_UNICODE_STRINGS;
+ }
+ if (capabilities & CAP_STATUS32) {
+ flags2 |= FLAGS2_32_BIT_ERROR_CODES;
+ }
+ if (capabilities & CAP_EXTENDED_SECURITY) {
+ flags2 |= FLAGS2_EXTENDED_SECURITY;
+ }
+ if (session->transport->negotiate.sign_info.doing_signing) {
+ flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+ }
+
+ SSVAL(req->out.hdr, HDR_FLG2, flags2);
+ SSVAL(req->out.hdr, HDR_PID, session->pid);
+ SSVAL(req->out.hdr, HDR_UID, session->vuid);
+
+ return req;
+}
+
+/*
+ setup a request for tree based commands
+*/
+struct cli_request *cli_request_setup(struct cli_tree *tree,
+ uint8 command,
+ unsigned wct, unsigned buflen)
+{
+ struct cli_request *req;
+
+ req = cli_request_setup_session(tree->session, command, wct, buflen);
+ if (req) {
+ req->tree = tree;
+ SSVAL(req->out.hdr,HDR_TID,tree->tid);
+ }
+ return req;
+}
+
+/*
+ grow the allocation of the data buffer portion of a reply
+ packet. Note that as this can reallocate the packet buffer this
+ invalidates any local pointers into the packet.
+
+ To cope with this req->out.ptr is supplied. This will be updated to
+ point at the same offset into the packet as before this call
+*/
+static void cli_req_grow_allocation(struct cli_request *req, unsigned new_size)
+{
+ int delta;
+ char *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->mem_ctx, req->out.buffer, 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 cli_req_grow_data(struct cli_request *req, unsigned new_size)
+{
+ int delta;
+
+ cli_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);
+}
+
+/*
+ send a message
+*/
+BOOL cli_request_send(struct cli_request *req)
+{
+ if (IVAL(req->out.buffer, 0) == 0) {
+ _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+ }
+
+ cli_request_calculate_sign_mac(req);
+
+ if (req->out.size != cli_sock_write(req->transport->socket, req->out.buffer, req->out.size)) {
+ req->transport->error.etype = ETYPE_SOCKET;
+ req->transport->error.e.socket_error = SOCKET_WRITE_ERROR;
+ DEBUG(0,("Error writing %d bytes to server - %s\n",
+ (int)req->out.size, strerror(errno)));
+ return False;
+ }
+
+ /* add it to the list of pending requests */
+ DLIST_ADD(req->transport->pending_requests, req);
+
+ return True;
+}
+
+
+/*
+ receive a response to a packet
+*/
+BOOL cli_request_receive(struct cli_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->in.buffer) {
+ if (!cli_transport_select(req->transport)) {
+ return False;
+ }
+
+ cli_request_receive_next(req->transport);
+ }
+
+ return True;
+}
+
+
+/*
+ handle oplock break requests from the server - return True if the request was
+ an oplock break
+*/
+static BOOL handle_oplock_break(struct cli_transport *transport, uint_t len, const char *hdr, const char *vwv)
+{
+ /* we must be very fussy about what we consider an oplock break to avoid
+ matching readbraw replies */
+ if (len != MIN_SMB_SIZE + VWV(8) ||
+ (CVAL(hdr, HDR_FLG) & FLAG_REPLY) ||
+ CVAL(hdr,HDR_COM) != SMBlockingX ||
+ SVAL(hdr, HDR_MID) != 0xFFFF ||
+ SVAL(vwv,VWV(6)) != 0 ||
+ SVAL(vwv,VWV(7)) != 0) {
+ return False;
+ }
+
+ if (transport->oplock.handler) {
+ uint16 tid = SVAL(hdr, HDR_TID);
+ uint16 fnum = SVAL(vwv,VWV(2));
+ uint8 level = CVAL(vwv,VWV(3));
+ transport->oplock.handler(transport, tid, fnum, level, transport->oplock.private);
+ }
+
+ return True;
+}
+
+
+/*
+ receive an async message from the server
+ this function assumes that the caller already knows that the socket is readable
+ and that there is a packet waiting
+
+ The packet is not actually returned by this function, instead any
+ registered async message handlers are called
+
+ return True if a packet was successfully received and processed
+ return False if the socket appears to be dead
+*/
+BOOL cli_request_receive_next(struct cli_transport *transport)
+{
+ BOOL ret;
+ int len;
+ char header[NBT_HDR_SIZE];
+ char *buffer, *hdr, *vwv;
+ TALLOC_CTX *mem_ctx;
+ struct cli_request *req;
+ uint16 wct, mid = 0;
+
+ len = cli_sock_read(transport->socket, header, 4);
+ if (len != 4) {
+ return False;
+ }
+
+ len = smb_len(header);
+
+ mem_ctx = talloc_init("cli_request_receive_next");
+
+ /* allocate the incoming buffer at the right size */
+ buffer = talloc(mem_ctx, len+NBT_HDR_SIZE);
+ if (!buffer) {
+ talloc_destroy(mem_ctx);
+ return False;
+ }
+
+ /* fill in the already received header */
+ memcpy(buffer, header, NBT_HDR_SIZE);
+
+ ret = cli_sock_read(transport->socket, buffer + NBT_HDR_SIZE, len);
+ /* If the server is not responding, note that now */
+ if (ret != len) {
+ return False;
+ }
+
+ hdr = buffer+NBT_HDR_SIZE;
+ vwv = hdr + HDR_VWV;
+
+ /* see if it could be an oplock break request */
+ if (handle_oplock_break(transport, len, hdr, vwv)) {
+ goto done;
+ }
+
+ /* at this point we need to check for a readbraw reply, as these can be any length */
+ if (transport->readbraw_pending) {
+ transport->readbraw_pending = 0;
+
+ /* it must match the first entry in the pending queue as the client is not allowed
+ to have outstanding readbraw requests */
+ req = transport->pending_requests;
+ if (!req) goto done;
+
+ req->in.buffer = buffer;
+ talloc_steal(mem_ctx, req->mem_ctx, buffer);
+ req->in.size = len + NBT_HDR_SIZE;
+ req->in.allocated = req->in.size;
+ goto async;
+ }
+
+ if (len >= MIN_SMB_SIZE) {
+ /* extract the mid for matching to pending requests */
+ mid = SVAL(hdr, HDR_MID);
+ wct = CVAL(hdr, HDR_WCT);
+ }
+
+ /* match the incoming request against the list of pending requests */
+ for (req=transport->pending_requests; req; req=req->next) {
+ if (req->mid == mid) break;
+ }
+
+ if (!req) {
+ DEBUG(3,("Discarding unmatched reply with mid %d\n", mid));
+ goto done;
+ }
+
+ /* fill in the 'in' portion of the matching request */
+ req->in.buffer = buffer;
+ talloc_steal(mem_ctx, req->mem_ctx, buffer);
+ req->in.size = len + NBT_HDR_SIZE;
+ req->in.allocated = req->in.size;
+
+ /* handle non-SMB replies */
+ if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE) {
+ goto done;
+ }
+
+ if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
+ DEBUG(2,("bad reply size for mid %d\n", mid));
+ req->status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ req->in.hdr = hdr;
+ req->in.vwv = vwv;
+ req->in.wct = wct;
+ if (req->in.size >= NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
+ req->in.data = req->in.vwv + VWV(wct) + 2;
+ req->in.data_size = SVAL(req->in.vwv, VWV(wct));
+ if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + req->in.data_size) {
+ DEBUG(3,("bad data size for mid %d\n", mid));
+ /* blergh - w2k3 gives a bogus data size values in some
+ openX replies */
+ req->in.data_size = req->in.size - (NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct));
+ }
+ }
+ req->in.ptr = req->in.data;
+ req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
+
+ if (!(req->flags2 & FLAGS2_32_BIT_ERROR_CODES)) {
+ transport->error.etype = ETYPE_DOS;
+ transport->error.e.dos.eclass = CVAL(req->in.hdr,HDR_RCLS);
+ transport->error.e.dos.ecode = SVAL(req->in.hdr,HDR_ERR);
+ req->status = dos_to_ntstatus(transport->error.e.dos.eclass,
+ transport->error.e.dos.ecode);
+ } else {
+ transport->error.etype = ETYPE_NT;
+ transport->error.e.nt_status = NT_STATUS(IVAL(req->in.hdr, HDR_RCLS));
+ req->status = transport->error.e.nt_status;
+ }
+
+ if (!cli_request_check_sign_mac(req)) {
+ transport->error.etype = ETYPE_SOCKET;
+ transport->error.e.socket_error = SOCKET_READ_BAD_SIG;
+ return False;
+ };
+
+async:
+ /* if this request has an async handler then call that to
+ notify that the reply has been received. This might destroy
+ the request so it must happen last */
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+
+done:
+ talloc_destroy(mem_ctx);
+ return True;
+}
+
+
+/*
+ wait for a reply to be received for a packet that just returns an error
+ code and nothing more
+*/
+NTSTATUS cli_request_simple_recv(struct cli_request *req)
+{
+ cli_request_receive(req);
+ return cli_request_destroy(req);
+}
+
+
+/* Return true if the last packet was in error */
+BOOL cli_request_is_error(struct cli_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 cli_req_append_string(struct cli_request *req, const char *str, unsigned 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;
+
+ cli_req_grow_allocation(req, len + req->out.data_size);
+
+ len = push_string(NULL, req->out.data + req->out.data_size, str, len, flags);
+
+ cli_req_grow_data(req, len + req->out.data_size);
+
+ return len;
+}
+
+/*
+ this is like cli_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 cli_req_append_string_len(struct cli_request *req, const char *str, unsigned 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 = cli_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 cli_req_append_ascii4(struct cli_request *req, const char *str, unsigned flags)
+{
+ size_t size;
+ cli_req_append_bytes(req, (const uint8 *)"\4", 1);
+ size = cli_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 cli_req_append_blob(struct cli_request *req, const DATA_BLOB *blob)
+{
+ cli_req_grow_allocation(req, req->out.data_size + blob->length);
+ memcpy(req->out.data + req->out.data_size, blob->data, blob->length);
+ cli_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 cli_req_append_bytes(struct cli_request *req, const uint8 *bytes, size_t byte_len)
+{
+ cli_req_grow_allocation(req, byte_len + req->out.data_size);
+ memcpy(req->out.data + req->out.data_size, bytes, byte_len);
+ cli_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 cli_req_append_var_block(struct cli_request *req, const uint8 *bytes, uint16 byte_len)
+{
+ cli_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);
+ }
+ cli_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 cli_req_pull_ucs2(struct cli_request *req, TALLOC_CTX *mem_ctx,
+ char **dest, const char *src, int byte_len, unsigned flags)
+{
+ int src_len, src_len2, alignment=0;
+ ssize_t ret;
+
+ if (!(flags & STR_NOALIGN) && ucs2_align(req->in.buffer, src, flags)) {
+ src++;
+ alignment=1;
+ if (byte_len != -1) {
+ byte_len--;
+ }
+ }
+
+ src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
+ if (src_len < 0) {
+ *dest = NULL;
+ return 0;
+ }
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+
+ src_len2 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2;
+ if (src_len2 < src_len - 2) {
+ /* include the termination if we didn't reach the end of the packet */
+ src_len2 += 2;
+ }
+
+ /* ucs2 strings must be at least 2 bytes long */
+ if (src_len2 < 2) {
+ *dest = NULL;
+ return 0;
+ }
+
+ ret = convert_string_talloc(mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest);
+ if (ret == -1) {
+ *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 cli_req_pull_ascii(struct cli_request *req, TALLOC_CTX *mem_ctx,
+ char **dest, const char *src, int byte_len, unsigned flags)
+{
+ int src_len, src_len2;
+ ssize_t ret;
+
+ src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
+ if (src_len < 0) {
+ *dest = NULL;
+ return 0;
+ }
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+ src_len2 = strnlen(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, (const void **)dest);
+
+ if (ret == -1) {
+ *dest = NULL;
+ return 0;
+ }
+
+ return ret;
+}
+
+/*
+ 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 cli_req_pull_string(struct cli_request *req, TALLOC_CTX *mem_ctx,
+ char **dest, const char *src, int byte_len, unsigned flags)
+{
+ if (!(flags & STR_ASCII) &&
+ ((flags & STR_UNICODE || (req->flags2 & FLAGS2_UNICODE_STRINGS)))) {
+ return cli_req_pull_ucs2(req, mem_ctx, dest, src, byte_len, flags);
+ }
+
+ return cli_req_pull_ascii(req, 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 cli_req_pull_blob(struct cli_request *req, TALLOC_CTX *mem_ctx, const char *src, int byte_len)
+{
+ int src_len;
+
+ src_len = req->in.data_size - PTR_DIFF(src, req->in.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 cli_req_data_oob(struct cli_request *req, const char *ptr, uint32 count)
+{
+ /* be careful with wraparound! */
+ if (ptr < req->in.data ||
+ ptr >= req->in.data + req->in.data_size ||
+ count > req->in.data_size ||
+ ptr + count > req->in.data + req->in.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 cli_raw_pull_data(struct cli_request *req, const char *src, int len, char *dest)
+{
+ if (len == 0) return True;
+
+ if (cli_req_data_oob(req, src, len)) {
+ return False;
+ }
+
+ memcpy(dest, src, len);
+ return True;
+}
+
+
+/*
+ put a NTTIME into a packet
+*/
+
+void cli_push_nttime(void *base, uint16 offset, NTTIME *t)
+{
+ SIVAL(base, offset, t->low);
+ SIVAL(base, offset+4, t->high);
+}
+
+/*
+ pull a NTTIME from a packet
+*/
+NTTIME cli_pull_nttime(void *base, uint16 offset)
+{
+ NTTIME ret;
+ ret.low = IVAL(base, offset);
+ ret.high = IVAL(base, offset+4);
+ 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
+*/
+static size_t cli_blob_pull_ucs2(TALLOC_CTX* mem_ctx,
+ DATA_BLOB *blob, const char **dest,
+ const char *src, int byte_len, unsigned flags)
+{
+ int src_len, src_len2, alignment=0;
+ ssize_t ret;
+
+ if (src < (const char *)blob->data ||
+ src >= (const char *)(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 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2;
+
+ if (src_len2 < src_len - 2) {
+ /* include the termination if we didn't reach the end of the packet */
+ src_len2 += 2;
+ }
+
+ ret = convert_string_talloc(mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest);
+ if (ret == -1) {
+ *dest = NULL;
+ return 0;
+ }
+
+ 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 cli_blob_pull_ascii(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob, const char **dest,
+ const char *src, int byte_len, unsigned flags)
+{
+ int src_len, src_len2;
+ ssize_t ret;
+
+ 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(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, (const void **)dest);
+
+ if (ret == -1) {
+ *dest = NULL;
+ return 0;
+ }
+
+ return ret;
+}
+
+/*
+ pull a string from a blob, returning a talloced 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 cli_blob_pull_string(struct cli_session *session,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ WIRE_STRING *dest,
+ uint16 len_offset, uint16 str_offset,
+ unsigned flags)
+{
+ dest->s = NULL;
+
+ if (len_offset > blob->length-4) {
+ return 0;
+ }
+ if (flags & STR_LEN8BIT) {
+ dest->private_length = CVAL(blob->data, len_offset);
+ } else {
+ dest->private_length = IVAL(blob->data, len_offset);
+ }
+ dest->s = NULL;
+ if (!(flags & STR_ASCII) &&
+ ((flags & STR_UNICODE) ||
+ (session->transport->negotiate.capabilities & CAP_UNICODE))) {
+ if ((str_offset&1) && !(flags & STR_NOALIGN)) {
+ str_offset++;
+ }
+ return cli_blob_pull_ucs2(mem_ctx, blob, &dest->s,
+ blob->data+str_offset, dest->private_length, flags);
+ }
+
+ return cli_blob_pull_ascii(mem_ctx, blob, &dest->s,
+ blob->data+str_offset, dest->private_length, flags);
+}
+
+/*
+ append a string into a blob
+*/
+size_t cli_blob_append_string(struct cli_session *session,
+ TALLOC_CTX *mem_ctx, DATA_BLOB *blob,
+ const char *str, unsigned 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, blob->length + max_len);
+ if (!blob->data) {
+ return 0;
+ }
+
+ len = push_string(NULL, blob->data + blob->length, str, max_len, flags);
+
+ blob->length += len;
+
+ return len;
+}
diff --git a/source4/libcli/raw/rawsearch.c b/source4/libcli/raw/rawsearch.c
new file mode 100644
index 0000000000..bdc39bb68c
--- /dev/null
+++ b/source4/libcli/raw/rawsearch.c
@@ -0,0 +1,569 @@
+/*
+ Unix SMB/CIFS implementation.
+ client directory search routines
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Old style search backend - process output.
+****************************************************************************/
+static void smb_raw_search_backend(struct cli_request *req,
+ TALLOC_CTX *mem_ctx,
+ uint16 count,
+ void *private,
+ BOOL (*callback)(void *private, union smb_search_data *file))
+
+{
+ union smb_search_data search_data;
+ int i;
+ char *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++) {
+ search_data.search.search_id = cli_req_pull_blob(req, mem_ctx, p, 21);
+ search_data.search.attrib = CVAL(p, 21);
+ search_data.search.write_time = make_unix_date(p + 22);
+ search_data.search.size = IVAL(p, 26);
+ cli_req_pull_ascii(req, mem_ctx, &search_data.search.name, p+30, 13, STR_ASCII);
+ if (!callback(private, &search_data)) {
+ break;
+ }
+ p += 43;
+ }
+}
+
+/****************************************************************************
+ Old style search first.
+****************************************************************************/
+static NTSTATUS smb_raw_search_first_old(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_first *io, void *private,
+ BOOL (*callback)(void *private, union smb_search_data *file))
+
+{
+ struct cli_request *req;
+
+ req = cli_request_setup(tree, SMBsearch, 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);
+ cli_req_append_ascii4(req, io->search_first.in.pattern, STR_TERMINATE);
+ cli_req_append_var_block(req, NULL, 0);
+
+ if (!cli_request_send(req) ||
+ !cli_request_receive(req)) {
+ return cli_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, callback);
+ }
+
+ return cli_request_destroy(req);
+}
+
+/****************************************************************************
+ Old style search next.
+****************************************************************************/
+static NTSTATUS smb_raw_search_next_old(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_next *io, void *private,
+ BOOL (*callback)(void *private, union smb_search_data *file))
+
+{
+ struct cli_request *req;
+
+ req = cli_request_setup(tree, SMBsearch, 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);
+ cli_req_append_ascii4(req, "", STR_TERMINATE);
+ cli_req_append_var_block(req, io->search_next.in.search_id.data, 21);
+
+ if (!cli_request_send(req) ||
+ !cli_request_receive(req)) {
+ return cli_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, callback);
+ }
+
+ return cli_request_destroy(req);
+}
+
+/****************************************************************************
+ Very raw search first - returns param/data blobs.
+****************************************************************************/
+static NTSTATUS smb_raw_search_first_blob(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx, /* used to allocate output blobs */
+ union smb_search_first *io,
+ uint16 info_level,
+ DATA_BLOB *out_param_blob,
+ DATA_BLOB *out_data_blob)
+{
+ struct smb_trans2 tp;
+ uint16 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 = 1024;
+ tp.in.max_data = 8192;
+ tp.in.setup = &setup;
+
+ 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, info_level);
+ SIVAL(tp.in.params.data, 8, io->t2ffirst.in.storage_type);
+
+ cli_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 cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_next *io,
+ uint16 info_level,
+ DATA_BLOB *out_param_blob,
+ DATA_BLOB *out_data_blob)
+{
+ struct smb_trans2 tp;
+ uint16 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 = 1024;
+ tp.in.max_data = 8192;
+ tp.in.setup = &setup;
+
+ 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, info_level);
+ SIVAL(tp.in.params.data, 6, io->t2fnext.in.resume_key);
+ SSVAL(tp.in.params.data, 10, io->t2fnext.in.flags);
+
+ cli_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 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 cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ enum search_level level,
+ uint16 flags,
+ DATA_BLOB *blob,
+ union smb_search_data *data)
+{
+ uint_t len, ofs;
+
+ switch (level) {
+ case RAW_SEARCH_GENERIC:
+ case RAW_SEARCH_SEARCH:
+ /* handled elsewhere */
+ return -1;
+
+ case RAW_SEARCH_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 = make_unix_date2(blob->data + 0);
+ data->standard.access_time = make_unix_date2(blob->data + 4);
+ data->standard.write_time = make_unix_date2(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 = cli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->standard.name,
+ 22, 23, STR_LEN8BIT);
+ return (len + 23 + 3) & ~3;
+
+ case RAW_SEARCH_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 = make_unix_date2(blob->data + 0);
+ data->ea_size.access_time = make_unix_date2(blob->data + 4);
+ data->ea_size.write_time = make_unix_date2(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 = cli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->ea_size.name,
+ 26, 27, STR_LEN8BIT | STR_NOALIGN);
+ return len + 27;
+
+ case RAW_SEARCH_DIRECTORY_INFO:
+ if (blob->length < 65) return -1;
+ ofs = IVAL(blob->data, 0);
+ data->directory_info.file_index = IVAL(blob->data, 4);
+ data->directory_info.create_time = cli_pull_nttime(blob->data, 8);
+ data->directory_info.access_time = cli_pull_nttime(blob->data, 16);
+ data->directory_info.write_time = cli_pull_nttime(blob->data, 24);
+ data->directory_info.change_time = cli_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 = cli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->directory_info.name,
+ 60, 64, 0);
+ if (ofs != 0 && ofs < 64+len) {
+ return -1;
+ }
+ return ofs;
+
+ case RAW_SEARCH_FULL_DIRECTORY_INFO:
+ if (blob->length < 69) return -1;
+ ofs = IVAL(blob->data, 0);
+ data->full_directory_info.file_index = IVAL(blob->data, 4);
+ data->full_directory_info.create_time = cli_pull_nttime(blob->data, 8);
+ data->full_directory_info.access_time = cli_pull_nttime(blob->data, 16);
+ data->full_directory_info.write_time = cli_pull_nttime(blob->data, 24);
+ data->full_directory_info.change_time = cli_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 = cli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->full_directory_info.name,
+ 60, 68, 0);
+ if (ofs != 0 && ofs < 68+len) {
+ return -1;
+ }
+ return ofs;
+
+ case RAW_SEARCH_NAME_INFO:
+ if (blob->length < 13) return -1;
+ ofs = IVAL(blob->data, 0);
+ data->name_info.file_index = IVAL(blob->data, 4);
+ len = cli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->name_info.name,
+ 8, 12, 0);
+ if (ofs != 0 && ofs < 12+len) {
+ return -1;
+ }
+ return ofs;
+
+
+ case RAW_SEARCH_BOTH_DIRECTORY_INFO:
+ if (blob->length < 95) return -1;
+ ofs = IVAL(blob->data, 0);
+ data->both_directory_info.file_index = IVAL(blob->data, 4);
+ data->both_directory_info.create_time = cli_pull_nttime(blob->data, 8);
+ data->both_directory_info.access_time = cli_pull_nttime(blob->data, 16);
+ data->both_directory_info.write_time = cli_pull_nttime(blob->data, 24);
+ data->both_directory_info.change_time = cli_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);
+ cli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->both_directory_info.short_name,
+ 68, 70, STR_LEN8BIT | STR_UNICODE);
+ len = cli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->both_directory_info.name,
+ 60, 94, 0);
+ if (ofs != 0 && ofs < 94+len) {
+ return -1;
+ }
+ return ofs;
+
+
+ case RAW_SEARCH_261:
+ if (blob->length < 81) return -1;
+ ofs = IVAL(blob->data, 0);
+ data->level_261.file_index = IVAL(blob->data, 4);
+ data->level_261.create_time = cli_pull_nttime(blob->data, 8);
+ data->level_261.access_time = cli_pull_nttime(blob->data, 16);
+ data->level_261.write_time = cli_pull_nttime(blob->data, 24);
+ data->level_261.change_time = cli_pull_nttime(blob->data, 32);
+ data->level_261.size = BVAL(blob->data, 40);
+ data->level_261.alloc_size = BVAL(blob->data, 48);
+ data->level_261.attrib = IVAL(blob->data, 56);
+ data->level_261.ea_size = IVAL(blob->data, 64);
+ data->level_261.unknown[0] = IVAL(blob->data, 68);
+ data->level_261.unknown[1] = IVAL(blob->data, 72);
+ data->level_261.unknown[2] = IVAL(blob->data, 76);
+ len = cli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->level_261.name,
+ 60, 80, 0);
+ if (ofs != 0 && ofs < 80+len) {
+ return -1;
+ }
+ return ofs;
+
+ case RAW_SEARCH_262:
+ if (blob->length < 105) return -1;
+ ofs = IVAL(blob->data, 0);
+ data->level_262.file_index = IVAL(blob->data, 4);
+ data->level_262.create_time = cli_pull_nttime(blob->data, 8);
+ data->level_262.access_time = cli_pull_nttime(blob->data, 16);
+ data->level_262.write_time = cli_pull_nttime(blob->data, 24);
+ data->level_262.change_time = cli_pull_nttime(blob->data, 32);
+ data->level_262.size = BVAL(blob->data, 40);
+ data->level_262.alloc_size = BVAL(blob->data, 48);
+ data->level_262.attrib = SVAL(blob->data, 56);
+ data->level_262.ea_size = IVAL(blob->data, 64);
+ cli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->level_262.short_name,
+ 68, 70, STR_LEN8BIT | STR_UNICODE);
+ data->level_262.unknown[0] = IVAL(blob->data, 94);
+ data->level_262.unknown[1] = IVAL(blob->data, 98);
+ len = cli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->level_262.name,
+ 60, 104, 0);
+ if (ofs != 0 && ofs < 104+len) {
+ return -1;
+ }
+ return ofs;
+ }
+
+ /* invalid level */
+ return -1;
+}
+
+/****************************************************************************
+ Trans2 search backend - process output.
+****************************************************************************/
+static NTSTATUS smb_raw_t2search_backend(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ enum search_level level,
+ uint16 flags,
+ int16 count,
+ DATA_BLOB *blob,
+ void *private,
+ BOOL (*callback)(void *private, union smb_search_data *file))
+
+{
+ int i;
+ DATA_BLOB blob2;
+
+ blob2.data = blob->data;
+ blob2.length = blob->length;
+
+ for (i=0; i < count; i++) {
+ union smb_search_data search_data;
+ uint_t len;
+
+ len = parse_trans2_search(tree, mem_ctx, level, flags, &blob2, &search_data);
+ if (len == -1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* the callback function can tell us that no more will
+ fit - in that case we stop, but it isn't an error */
+ if (!callback(private, &search_data)) {
+ break;
+ }
+
+ if (len == 0) break;
+
+ blob2.data += len;
+ blob2.length -= len;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/* Implements trans2findfirst2 and old search
+ */
+NTSTATUS smb_raw_search_first(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_first *io, void *private,
+ BOOL (*callback)(void *private, union smb_search_data *file))
+{
+ uint16 info_level = 0;
+ DATA_BLOB p_blob, d_blob;
+ NTSTATUS status;
+
+ if (io->generic.level == RAW_SEARCH_SEARCH) {
+ return smb_raw_search_first_old(tree, mem_ctx, io, private, callback);
+ }
+ if (io->generic.level >= RAW_SEARCH_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+ info_level = (uint16)io->generic.level;
+
+ status = smb_raw_search_first_blob(tree, mem_ctx,
+ io, info_level, &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",
+ 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.level,
+ io->t2ffirst.in.flags, io->t2ffirst.out.count,
+ &d_blob, private, callback);
+
+ return status;
+}
+
+/* Implements trans2findnext2 and old smbsearch
+ */
+NTSTATUS smb_raw_search_next(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_next *io, void *private,
+ BOOL (*callback)(void *private, union smb_search_data *file))
+{
+ uint16 info_level = 0;
+ DATA_BLOB p_blob, d_blob;
+ NTSTATUS status;
+
+ if (io->generic.level == RAW_SEARCH_SEARCH) {
+ return smb_raw_search_next_old(tree, mem_ctx, io, private, callback);
+ }
+ if (io->generic.level >= RAW_SEARCH_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+ info_level = (uint16)io->generic.level;
+
+ status = smb_raw_search_next_blob(tree, mem_ctx,
+ io, info_level, &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",
+ 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.level,
+ io->t2fnext.in.flags, io->t2fnext.out.count,
+ &d_blob, private, callback);
+
+ return status;
+}
+
+/*
+ Implements trans2findclose2
+ */
+NTSTATUS smb_raw_search_close(struct cli_tree *tree,
+ union smb_search_close *io)
+{
+ struct cli_request *req;
+
+ req = cli_request_setup(tree, SMBfindclose, 1, 0);
+ if (!req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(req->out.vwv, VWV(0), io->findclose.in.handle);
+
+ if (cli_request_send(req)) {
+ cli_request_receive(req);
+ }
+
+ return cli_request_destroy(req);
+}
diff --git a/source4/libcli/raw/rawsetfileinfo.c b/source4/libcli/raw/rawsetfileinfo.c
new file mode 100644
index 0000000000..4044686c64
--- /dev/null
+++ b/source4/libcli/raw/rawsetfileinfo.c
@@ -0,0 +1,335 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAW_SFILEINFO_* calls
+ Copyright (C) James Myers 2003
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Handle qfileinfo/qpathinfo trans2 backend.
+****************************************************************************/
+static BOOL smb_raw_setinfo_backend(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_setfileinfo *parms,
+ DATA_BLOB *blob)
+{
+ uint_t len;
+
+#define NEED_BLOB(n) do { \
+ *blob = data_blob_talloc(mem_ctx, NULL, n); \
+ if (blob->data == NULL) return False; \
+ } while (0)
+
+ switch (parms->generic.level) {
+ case RAW_SFILEINFO_GENERIC:
+ case RAW_SFILEINFO_SETATTR:
+ case RAW_SFILEINFO_SETATTRE:
+ /* not handled here */
+ return False;
+
+ case RAW_SFILEINFO_STANDARD:
+ NEED_BLOB(12);
+ put_dos_date2(blob->data, 0, parms->standard.in.create_time);
+ put_dos_date2(blob->data, 4, parms->standard.in.access_time);
+ put_dos_date2(blob->data, 8, parms->standard.in.write_time);
+ return True;
+
+ case RAW_SFILEINFO_EA_SET:
+ NEED_BLOB(ea_list_size(1, &parms->ea_set.in.ea));
+ ea_put_list(blob->data, 1, &parms->ea_set.in.ea);
+ return True;
+
+ case RAW_SFILEINFO_BASIC_INFO:
+ case RAW_SFILEINFO_BASIC_INFORMATION:
+ NEED_BLOB(40);
+ cli_push_nttime(blob->data, 0, &parms->basic_info.in.create_time);
+ cli_push_nttime(blob->data, 8, &parms->basic_info.in.access_time);
+ cli_push_nttime(blob->data, 16, &parms->basic_info.in.write_time);
+ cli_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_UNIX_BASIC:
+ NEED_BLOB(92);
+ SBVAL(blob->data, 0, parms->unix_basic.in.end_of_file);
+ SBVAL(blob->data, 8, parms->unix_basic.in.num_bytes);
+ cli_push_nttime(blob->data, 16, &parms->unix_basic.in.status_change_time);
+ cli_push_nttime(blob->data, 24, &parms->unix_basic.in.access_time);
+ cli_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.nlink);
+ return True;
+
+ case RAW_SFILEINFO_DISPOSITION_INFO:
+ 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_INFO:
+ 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_INFO:
+ 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 = cli_blob_append_string(tree->session, mem_ctx, blob,
+ parms->rename_information.in.new_name,
+ STR_UNICODE|STR_TERMINATE);
+ SIVAL(blob->data, 8, 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;
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Very raw set file info - takes data blob (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_setfileinfo_blob_send(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ uint16 fnum,
+ uint16 info_level,
+ DATA_BLOB *blob)
+{
+ struct smb_trans2 tp;
+ uint16 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 cli_request *smb_raw_setpathinfo_blob_send(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ const char *fname,
+ uint16 info_level,
+ DATA_BLOB *blob)
+{
+ struct smb_trans2 tp;
+ uint16 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, 4);
+ if (!tp.in.params.data) {
+ return NULL;
+ }
+ SSVAL(tp.in.params.data, 0, info_level);
+ SSVAL(tp.in.params.data, 2, 0);
+ cli_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 cli_request *smb_raw_setattr_send(struct cli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ struct cli_request *req;
+
+ req = cli_request_setup(tree, SMBsetatr, 8, 0);
+ if (!req) return NULL;
+
+ SSVAL(req->out.vwv, VWV(0), parms->setattr.in.attrib);
+ put_dos_date3(req->out.vwv, VWV(1), parms->setattr.in.write_time);
+ memset(req->out.vwv + VWV(3), 0, 10); /* reserved */
+ cli_req_append_ascii4(req, parms->setattr.file.fname, STR_TERMINATE);
+ cli_req_append_ascii4(req, "", STR_TERMINATE);
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Handle setattrE. (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_setattrE_send(struct cli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ struct cli_request *req;
+
+ req = cli_request_setup(tree, SMBsetattrE, 7, 0);
+ if (!req) return NULL;
+
+ SSVAL(req->out.vwv, VWV(0), parms->setattre.file.fnum);
+ put_dos_date2(req->out.vwv, VWV(1), parms->setattre.in.create_time);
+ put_dos_date2(req->out.vwv, VWV(3), parms->setattre.in.access_time);
+ put_dos_date2(req->out.vwv, VWV(5), parms->setattre.in.write_time);
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Set file info (async send)
+****************************************************************************/
+struct cli_request *smb_raw_setfileinfo_send(struct cli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ DATA_BLOB blob;
+ TALLOC_CTX *mem_ctx;
+ struct cli_request *req;
+
+ if (parms->generic.level == RAW_SFILEINFO_SETATTRE) {
+ return smb_raw_setattrE_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_destroy(mem_ctx);
+ return NULL;
+ }
+
+ /* send request and process the output */
+ req = smb_raw_setfileinfo_blob_send(tree,
+ mem_ctx,
+ parms->generic.file.fnum,
+ parms->generic.level,
+ &blob);
+
+ talloc_destroy(mem_ctx);
+ return req;
+}
+
+/****************************************************************************
+ Set file info (async send)
+****************************************************************************/
+NTSTATUS smb_raw_setfileinfo(struct cli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ struct cli_request *req = smb_raw_setfileinfo_send(tree, parms);
+ return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Set path info (async send)
+****************************************************************************/
+struct cli_request *smb_raw_setpathinfo_send(struct cli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ DATA_BLOB blob;
+ TALLOC_CTX *mem_ctx;
+ struct cli_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_destroy(mem_ctx);
+ return NULL;
+ }
+
+ /* send request and process the output */
+ req = smb_raw_setpathinfo_blob_send(tree,
+ mem_ctx,
+ parms->generic.file.fname,
+ parms->generic.level,
+ &blob);
+
+ talloc_destroy(mem_ctx);
+ return req;
+}
+
+/****************************************************************************
+ Set path info (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_setpathinfo(struct cli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ struct cli_request *req = smb_raw_setpathinfo_send(tree, parms);
+ return cli_request_simple_recv(req);
+}
diff --git a/source4/libcli/raw/rawtrans.c b/source4/libcli/raw/rawtrans.c
new file mode 100644
index 0000000000..508f920268
--- /dev/null
+++ b/source4/libcli/raw/rawtrans.c
@@ -0,0 +1,489 @@
+/*
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+
+/*
+ check out of bounds for incoming data
+*/
+static BOOL raw_trans_oob(struct cli_request *req,
+ uint_t offset, uint_t count)
+{
+ char *ptr;
+
+ if (count == 0) {
+ return False;
+ }
+
+ ptr = req->in.hdr + offset;
+
+ /* be careful with wraparound! */
+ if (ptr < req->in.data ||
+ ptr >= req->in.data + req->in.data_size ||
+ count > req->in.data_size ||
+ ptr + count > req->in.data + req->in.data_size) {
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ receive a SMB trans or trans2 response allocating the necessary memory
+ ****************************************************************************/
+NTSTATUS smb_raw_trans2_recv(struct cli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ int total_data=0;
+ int total_param=0;
+ char *tdata;
+ char *tparam;
+
+ parms->out.data.length = 0;
+ parms->out.data.data = NULL;
+ parms->out.params.length = 0;
+ parms->out.params.data = NULL;
+
+ if (!cli_request_receive(req)) {
+ req->status = NT_STATUS_UNSUCCESSFUL;
+ return cli_request_destroy(req);
+ }
+
+ /*
+ * An NT RPC pipe call can return ERRDOS, ERRmoredata
+ * to a trans call. This is not an error and should not
+ * be treated as such.
+ */
+ if (NT_STATUS_IS_ERR(req->status)) {
+ return cli_request_destroy(req);
+ }
+
+ CLI_CHECK_MIN_WCT(req, 10);
+
+ /* parse out the lengths */
+ total_data = SVAL(req->in.vwv, VWV(1));
+ total_param = SVAL(req->in.vwv, VWV(0));
+
+ /* allocate it */
+ if (total_data != 0) {
+ tdata = talloc_realloc(mem_ctx, parms->out.data.data,total_data);
+ if (!tdata) {
+ DEBUG(0,("smb_raw_receive_trans: failed to enlarge data buffer to %d bytes\n", total_data));
+ req->status = NT_STATUS_NO_MEMORY;
+ return cli_request_destroy(req);
+ }
+ parms->out.data.data = tdata;
+ }
+
+ if (total_param != 0) {
+ tparam = talloc_realloc(mem_ctx, parms->out.params.data,total_param);
+ if (!tparam) {
+ DEBUG(0,("smb_raw_receive_trans: failed to enlarge param buffer to %d bytes\n", total_param));
+ req->status = NT_STATUS_NO_MEMORY;
+ return cli_request_destroy(req);
+ }
+ parms->out.params.data = tparam;
+ }
+
+ parms->out.setup_count = SVAL(req->in.vwv, VWV(9));
+ CLI_CHECK_WCT(req, 10 + parms->out.setup_count);
+
+ if (parms->out.setup_count > 0) {
+ int i;
+ parms->out.setup = talloc(mem_ctx, 2 * parms->out.setup_count);
+ if (!parms->out.setup) {
+ req->status = NT_STATUS_NO_MEMORY;
+ return cli_request_destroy(req);
+ }
+ for (i=0;i<parms->out.setup_count;i++) {
+ parms->out.setup[i] = SVAL(req->in.vwv, VWV(10+i));
+ }
+ }
+
+ while (1) {
+ uint16 param_count, param_ofs, param_disp;
+ uint16 data_count, data_ofs, data_disp;
+ uint16 total_data2, total_param2;
+
+ /* parse out the total lengths again - they can shrink! */
+ total_data2 = SVAL(req->in.vwv, VWV(1));
+ total_param2 = SVAL(req->in.vwv, VWV(0));
+
+ if (total_data2 > total_data ||
+ total_param2 > total_param) {
+ /* they must *only* shrink */
+ DEBUG(1,("smb_raw_receive_trans: data/params expanded!\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ return cli_request_destroy(req);
+ }
+
+ total_data = total_data2;
+ total_param = total_param2;
+
+ /* parse params for this lump */
+ param_count = SVAL(req->in.vwv, VWV(3));
+ param_ofs = SVAL(req->in.vwv, VWV(4));
+ param_disp = SVAL(req->in.vwv, VWV(5));
+
+ data_count = SVAL(req->in.vwv, VWV(6));
+ data_ofs = SVAL(req->in.vwv, VWV(7));
+ data_disp = SVAL(req->in.vwv, VWV(8));
+
+ if (data_count + data_disp > total_data ||
+ param_count + param_disp > total_param) {
+ DEBUG(1,("smb_raw_receive_trans: Buffer overflow\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ return cli_request_destroy(req);
+ }
+
+ /* check the server isn't being nasty */
+ if (raw_trans_oob(req, param_ofs, param_count) ||
+ raw_trans_oob(req, data_ofs, data_count)) {
+ DEBUG(1,("smb_raw_receive_trans: out of bounds parameters!\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ return cli_request_destroy(req);
+ }
+
+ if (data_count) {
+ memcpy(parms->out.data.data + data_disp,
+ req->in.hdr + data_ofs,
+ data_count);
+ }
+
+ if (param_count) {
+ memcpy(parms->out.params.data + param_disp,
+ req->in.hdr + param_ofs,
+ param_count);
+ }
+
+ parms->out.data.length += data_count;
+ parms->out.params.length += param_count;
+
+ if (total_data <= parms->out.data.length && total_param <= parms->out.params.length)
+ break;
+
+ /* to receive more requests we need to mark this request as not received */
+ req->in.buffer = NULL;
+
+ if (!cli_request_receive(req)) {
+ req->status = NT_STATUS_UNSUCCESSFUL;
+ return cli_request_destroy(req);
+ }
+ }
+
+failed:
+ return cli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ trans2 raw async interface - only BLOBs used in this interface.
+note that this doesn't yet support multi-part requests
+****************************************************************************/
+struct cli_request *smb_raw_trans2_send(struct cli_tree *tree,
+ struct smb_trans2 *parms)
+{
+ uint8 command = SMBtrans2;
+ int wct = 14 + parms->in.setup_count;
+ struct cli_request *req;
+ char *outdata,*outparam;
+ int data_sent, param_sent;
+ int i;
+ const int padding = 3;
+
+ req = cli_request_setup(tree, command, wct, padding);
+ if (!req) {
+ return NULL;
+ }
+
+ /* fill in SMB parameters */
+ data_sent = parms->in.data.length;
+ param_sent = parms->in.params.length;
+ outparam = req->out.data + padding;
+ outdata = outparam + param_sent;
+
+ /* make sure we don't leak data via the padding */
+ memset(req->out.data, 0, padding);
+
+ /* primary request */
+ SSVAL(req->out.vwv,VWV(0),parms->in.params.length);
+ SSVAL(req->out.vwv,VWV(1),parms->in.data.length);
+ SSVAL(req->out.vwv,VWV(2),parms->in.max_param);
+ SSVAL(req->out.vwv,VWV(3),parms->in.max_data);
+ SSVAL(req->out.vwv,VWV(4),parms->in.max_setup);
+ SSVAL(req->out.vwv,VWV(5),parms->in.flags);
+ SIVAL(req->out.vwv,VWV(6),parms->in.timeout);
+ SSVAL(req->out.vwv,VWV(8),0); /* reserved */
+ SSVAL(req->out.vwv,VWV(9),parms->in.params.length);
+ SSVAL(req->out.vwv,VWV(10),PTR_DIFF(outparam,req->out.hdr));
+ SSVAL(req->out.vwv,VWV(11),parms->in.data.length);
+ SSVAL(req->out.vwv,VWV(12),PTR_DIFF(outdata,req->out.hdr));
+ SSVAL(req->out.vwv,VWV(13),parms->in.setup_count);
+ for (i=0;i<parms->in.setup_count;i++) {
+ SSVAL(req->out.vwv,VWV(14)+i*2,parms->in.setup[i]);
+ }
+ if (parms->in.params.data) {
+ cli_req_append_blob(req, &parms->in.params);
+ }
+ if (parms->in.data.data) {
+ cli_req_append_blob(req, &parms->in.data);
+ }
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/*
+ trans2 synchronous blob interface
+*/
+NTSTATUS smb_raw_trans2(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ struct cli_request *req;
+ req = smb_raw_trans2_send(tree, parms);
+ if (!req) return NT_STATUS_UNSUCCESSFUL;
+ return smb_raw_trans2_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ receive a SMB nttrans response allocating the necessary memory
+ ****************************************************************************/
+NTSTATUS smb_raw_nttrans_recv(struct cli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_nttrans *parms)
+{
+ uint32 total_data, recvd_data=0;
+ uint32 total_param, recvd_param=0;
+
+ if (!cli_request_receive(req) ||
+ cli_request_is_error(req)) {
+ return cli_request_destroy(req);
+ }
+
+ /* sanity check */
+ if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) {
+ DEBUG(0,("smb_raw_receive_nttrans: Expected %s response, got command 0x%02x\n",
+ "SMBnttrans",
+ CVAL(req->in.hdr,HDR_COM)));
+ req->status = NT_STATUS_UNSUCCESSFUL;
+ return cli_request_destroy(req);
+ }
+
+ CLI_CHECK_MIN_WCT(req, 18);
+
+ /* parse out the lengths */
+ total_param = IVAL(req->in.vwv, 3);
+ total_data = IVAL(req->in.vwv, 7);
+
+ parms->out.data = data_blob_talloc(mem_ctx, NULL, total_data);
+ parms->out.params = data_blob_talloc(mem_ctx, NULL, total_param);
+
+ if (parms->out.data.length != total_data ||
+ parms->out.params.length != total_param) {
+ req->status = NT_STATUS_NO_MEMORY;
+ return cli_request_destroy(req);
+ }
+
+ parms->out.setup_count = CVAL(req->in.vwv, 35);
+ CLI_CHECK_WCT(req, 18 + parms->out.setup_count);
+
+ if (parms->out.setup_count > 0) {
+ int i;
+ parms->out.setup = talloc(mem_ctx, 2 * parms->out.setup_count);
+ if (!parms->out.setup) {
+ req->status = NT_STATUS_NO_MEMORY;
+ return cli_request_destroy(req);
+ }
+ for (i=0;i<parms->out.setup_count;i++) {
+ parms->out.setup[i] = SVAL(req->in.vwv, VWV(18+i));
+ }
+ }
+
+ while (recvd_data < total_data ||
+ recvd_param < total_param) {
+ uint32 param_count, param_ofs, param_disp;
+ uint32 data_count, data_ofs, data_disp;
+ uint32 total_data2, total_param2;
+
+ /* parse out the total lengths again - they can shrink! */
+ total_param2 = IVAL(req->in.vwv, 3);
+ total_data2 = IVAL(req->in.vwv, 7);
+
+ if (total_data2 > total_data ||
+ total_param2 > total_param) {
+ /* they must *only* shrink */
+ DEBUG(1,("smb_raw_receive_nttrans: data/params expanded!\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ return cli_request_destroy(req);
+ }
+
+ total_data = total_data2;
+ total_param = total_param2;
+ parms->out.data.length = total_data;
+ parms->out.params.length = total_param;
+
+ /* parse params for this lump */
+ param_count = IVAL(req->in.vwv, 11);
+ param_ofs = IVAL(req->in.vwv, 15);
+ param_disp = IVAL(req->in.vwv, 19);
+
+ data_count = IVAL(req->in.vwv, 23);
+ data_ofs = IVAL(req->in.vwv, 27);
+ data_disp = IVAL(req->in.vwv, 31);
+
+ if (data_count + data_disp > total_data ||
+ param_count + param_disp > total_param) {
+ DEBUG(1,("smb_raw_receive_nttrans: Buffer overflow\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ return cli_request_destroy(req);
+ }
+
+ /* check the server isn't being nasty */
+ if (raw_trans_oob(req, param_ofs, param_count) ||
+ raw_trans_oob(req, data_ofs, data_count)) {
+ DEBUG(1,("smb_raw_receive_nttrans: out of bounds parameters!\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ return cli_request_destroy(req);
+ }
+
+ if (data_count) {
+ memcpy(parms->out.data.data + data_disp,
+ req->in.hdr + data_ofs,
+ data_count);
+ }
+
+ if (param_count) {
+ memcpy(parms->out.params.data + param_disp,
+ req->in.hdr + param_ofs,
+ param_count);
+ }
+
+ recvd_param += param_count;
+ recvd_data += data_count;
+
+ if (recvd_data >= total_data &&
+ recvd_param >= total_param) {
+ break;
+ }
+
+ if (!cli_request_receive(req) ||
+ cli_request_is_error(req)) {
+ return cli_request_destroy(req);
+ }
+
+ /* sanity check */
+ if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) {
+ DEBUG(0,("smb_raw_receive_nttrans: Expected nttranss, got command 0x%02x\n",
+ CVAL(req->in.hdr, HDR_COM)));
+ req->status = NT_STATUS_UNSUCCESSFUL;
+ return cli_request_destroy(req);
+ }
+ }
+
+failed:
+ return cli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ nttrans raw - only BLOBs used in this interface.
+ at the moment we only handle a single primary request
+****************************************************************************/
+struct cli_request *smb_raw_nttrans_send(struct cli_tree *tree,
+ struct smb_nttrans *parms)
+{
+ struct cli_request *req;
+ char *outdata, *outparam;
+ int i;
+ int align = 0;
+
+ /* only align if there are parameters or data */
+ if (parms->in.params.length || parms->in.data.length) {
+ align = 3;
+ }
+
+ req = cli_request_setup(tree, SMBnttrans,
+ 19 + parms->in.setup_count,
+ align +
+ parms->in.params.length +
+ parms->in.data.length);
+ if (!req) {
+ return NULL;
+ }
+
+ /* fill in SMB parameters */
+ outparam = req->out.data + align;
+ outdata = outparam + parms->in.params.length;
+
+ SCVAL(req->out.vwv, 0, parms->in.max_setup);
+ SSVAL(req->out.vwv, 1, 0); /* reserved */
+ SIVAL(req->out.vwv, 3, parms->in.params.length);
+ SIVAL(req->out.vwv, 7, parms->in.data.length);
+ SIVAL(req->out.vwv, 11, parms->in.max_param);
+ SIVAL(req->out.vwv, 15, parms->in.max_data);
+ SIVAL(req->out.vwv, 19, parms->in.params.length);
+ SIVAL(req->out.vwv, 23, PTR_DIFF(outparam,req->out.hdr));
+ SIVAL(req->out.vwv, 27, parms->in.data.length);
+ SIVAL(req->out.vwv, 31, PTR_DIFF(outdata,req->out.hdr));
+ SCVAL(req->out.vwv, 35, parms->in.setup_count);
+ SSVAL(req->out.vwv, 36, parms->in.function);
+ for (i=0;i<parms->in.setup_count;i++) {
+ SSVAL(req->out.vwv,VWV(19+i),parms->in.setup[i]);
+ }
+ if (parms->in.params.length) {
+ memcpy(outparam, parms->in.params.data, parms->in.params.length);
+ }
+ if (parms->in.data.length) {
+ memcpy(outparam, parms->in.data.data, parms->in.data.length);
+ }
+
+ if (!cli_request_send(req)) {
+ cli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ receive a SMB nttrans response allocating the necessary memory
+ ****************************************************************************/
+NTSTATUS smb_raw_nttrans(struct cli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_nttrans *parms)
+{
+ struct cli_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/smb_signing.c b/source4/libcli/raw/smb_signing.c
new file mode 100644
index 0000000000..2ab61aa001
--- /dev/null
+++ b/source4/libcli/raw/smb_signing.c
@@ -0,0 +1,341 @@
+/*
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+struct smb_basic_signing_context {
+ DATA_BLOB mac_key;
+ uint32 next_seq_num;
+};
+
+/***********************************************************
+ SMB signing - Common code before we set a new signing implementation
+************************************************************/
+static BOOL set_smb_signing_common(struct cli_transport *transport)
+{
+ if (!(transport->negotiate.sec_mode &
+ (NEGOTIATE_SECURITY_SIGNATURES_REQUIRED|NEGOTIATE_SECURITY_SIGNATURES_ENABLED))) {
+ return False;
+ }
+
+ if (transport->negotiate.sign_info.doing_signing) {
+ return False;
+ }
+
+ if (transport->negotiate.sign_info.free_signing_context)
+ transport->negotiate.sign_info.free_signing_context(transport);
+
+ /* These calls are INCOMPATIBLE with SMB signing */
+ transport->negotiate.readbraw_supported = False;
+ transport->negotiate.writebraw_supported = False;
+
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Common code for 'real' implementations
+************************************************************/
+static BOOL set_smb_signing_real_common(struct cli_transport *transport)
+{
+ if (transport->negotiate.sec_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) {
+ DEBUG(5, ("Mandatory SMB signing enabled!\n"));
+ transport->negotiate.sign_info.doing_signing = True;
+ }
+
+ DEBUG(5, ("SMB signing enabled!\n"));
+
+ return True;
+}
+
+static void mark_packet_signed(struct cli_request *req)
+{
+ uint16 flags2;
+ flags2 = SVAL(req->out.hdr, HDR_FLG2);
+ flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+ SSVAL(req->out.hdr, HDR_FLG2, flags2);
+}
+
+static BOOL signing_good(struct cli_request *req, BOOL good)
+{
+ if (good && !req->transport->negotiate.sign_info.doing_signing) {
+ req->transport->negotiate.sign_info.doing_signing = True;
+ }
+
+ if (!good) {
+ if (req->transport->negotiate.sign_info.doing_signing) {
+ DEBUG(1, ("SMB signature check failed!\n"));
+ return False;
+ } else {
+ DEBUG(3, ("Server did not sign reply correctly\n"));
+ cli_transport_free_signing_context(req->transport);
+ return False;
+ }
+ }
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - calculate a MAC to send.
+************************************************************/
+static void cli_request_simple_sign_outgoing_message(struct cli_request *req)
+{
+ unsigned char calc_md5_mac[16];
+ struct MD5Context md5_ctx;
+ struct smb_basic_signing_context *data = req->transport->negotiate.sign_info.signing_context;
+
+#if 0
+ /* enable this when packet signing is preventing you working out why valgrind
+ says that data is uninitialised */
+ file_save("pkt.dat", req->out.buffer, req->out.size);
+#endif
+
+ req->seq_num = data->next_seq_num;
+
+ /* some requests (eg. NTcancel) are one way, and the sequence number
+ should be increased by 1 not 2 */
+ if (req->one_way_request) {
+ data->next_seq_num += 1;
+ } else {
+ data->next_seq_num += 2;
+ }
+
+ /*
+ * Firstly put the sequence number into the first 4 bytes.
+ * and zero out the next 4 bytes.
+ */
+ SIVAL(req->out.hdr, HDR_SS_FIELD, req->seq_num);
+ SIVAL(req->out.hdr, HDR_SS_FIELD + 4, 0);
+
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(req);
+
+ /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, data->mac_key.data,
+ data->mac_key.length);
+ MD5Update(&md5_ctx,
+ req->out.buffer + NBT_HDR_SIZE,
+ req->out.size - NBT_HDR_SIZE);
+ MD5Final(calc_md5_mac, &md5_ctx);
+
+ memcpy(&req->out.hdr[HDR_SS_FIELD], calc_md5_mac, 8);
+
+/* req->out.hdr[HDR_SS_FIELD+2]=0;
+ Uncomment this to test if the remote server actually verifies signitures...*/
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - check a MAC sent by server.
+************************************************************/
+static BOOL cli_request_simple_check_incoming_message(struct cli_request *req)
+{
+ BOOL good;
+ unsigned char calc_md5_mac[16];
+ unsigned char server_sent_mac[8];
+ unsigned char sequence_buf[8];
+ struct MD5Context md5_ctx;
+ struct smb_basic_signing_context *data = req->transport->negotiate.sign_info.signing_context;
+ const size_t offset_end_of_sig = (HDR_SS_FIELD + 8);
+ int i;
+ const int sign_range = 0;
+
+ /* its quite bogus to be guessing sequence numbers, but very useful
+ when debugging signing implementations */
+ for (i = 1-sign_range; i <= 1+sign_range; i++) {
+ /*
+ * Firstly put the sequence number into the first 4 bytes.
+ * and zero out the next 4 bytes.
+ */
+ SIVAL(sequence_buf, 0, req->seq_num+i);
+ SIVAL(sequence_buf, 4, 0);
+
+ /* get a copy of the server-sent mac */
+ memcpy(server_sent_mac, &req->in.hdr[HDR_SS_FIELD], sizeof(server_sent_mac));
+
+ /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, data->mac_key.data,
+ data->mac_key.length);
+ MD5Update(&md5_ctx, req->in.hdr, HDR_SS_FIELD);
+ MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
+
+ MD5Update(&md5_ctx, req->in.hdr + offset_end_of_sig,
+ req->in.size - NBT_HDR_SIZE - (offset_end_of_sig));
+ MD5Final(calc_md5_mac, &md5_ctx);
+
+ good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
+ if (good) break;
+ }
+
+ if (good && i != 1) {
+ DEBUG(0,("SIGNING OFFSET %d\n", i));
+ }
+
+ if (!good) {
+ DEBUG(5, ("cli_request_simple_check_incoming_message: BAD SIG: wanted SMB signature of\n"));
+ dump_data(5, calc_md5_mac, 8);
+
+ DEBUG(5, ("cli_request_simple_check_incoming_message: BAD SIG: got SMB signature of\n"));
+ dump_data(5, server_sent_mac, 8);
+ }
+ return signing_good(req, good);
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - free signing context
+************************************************************/
+static void cli_transport_simple_free_signing_context(struct cli_transport *transport)
+{
+ struct smb_basic_signing_context *data = transport->negotiate.sign_info.signing_context;
+
+ data_blob_free(&data->mac_key);
+ SAFE_FREE(transport->negotiate.sign_info.signing_context);
+
+ return;
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - setup the MAC key.
+************************************************************/
+BOOL cli_transport_simple_set_signing(struct cli_transport *transport,
+ const uchar user_transport_key[16], const DATA_BLOB response)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!set_smb_signing_common(transport)) {
+ return False;
+ }
+
+ if (!set_smb_signing_real_common(transport)) {
+ return False;
+ }
+
+ data = smb_xmalloc(sizeof(*data));
+ transport->negotiate.sign_info.signing_context = data;
+
+ data->mac_key = data_blob(NULL, MIN(response.length + 16, 40));
+
+ memcpy(&data->mac_key.data[0], user_transport_key, 16);
+ memcpy(&data->mac_key.data[16],response.data, MIN(response.length, 40 - 16));
+
+ /* Initialise the sequence number */
+ data->next_seq_num = 0;
+
+ transport->negotiate.sign_info.sign_outgoing_message = cli_request_simple_sign_outgoing_message;
+ transport->negotiate.sign_info.check_incoming_message = cli_request_simple_check_incoming_message;
+ transport->negotiate.sign_info.free_signing_context = cli_transport_simple_free_signing_context;
+
+ return True;
+}
+
+
+/***********************************************************
+ SMB signing - NULL implementation - calculate a MAC to send.
+************************************************************/
+static void cli_request_null_sign_outgoing_message(struct cli_request *req)
+{
+ /* we can't zero out the sig, as we might be trying to send a
+ transport request - which is NBT-level, not SMB level and doesn't
+ have the field */
+}
+
+
+/***********************************************************
+ SMB signing - NULL implementation - check a MAC sent by server.
+************************************************************/
+static BOOL cli_request_null_check_incoming_message(struct cli_request *req)
+{
+ return True;
+}
+
+
+/***********************************************************
+ SMB signing - NULL implementation - free signing context
+************************************************************/
+static void cli_null_free_signing_context(struct cli_transport *transport)
+{
+}
+
+/**
+ SMB signing - NULL implementation - setup the MAC key.
+
+ @note Used as an initialisation only - it will not correctly
+ shut down a real signing mechanism
+*/
+BOOL cli_null_set_signing(struct cli_transport *transport)
+{
+ transport->negotiate.sign_info.signing_context = NULL;
+
+ transport->negotiate.sign_info.sign_outgoing_message = cli_request_null_sign_outgoing_message;
+ transport->negotiate.sign_info.check_incoming_message = cli_request_null_check_incoming_message;
+ transport->negotiate.sign_info.free_signing_context = cli_null_free_signing_context;
+
+ return True;
+}
+
+
+/**
+ * Free the signing context
+ */
+void cli_transport_free_signing_context(struct cli_transport *transport)
+{
+ if (transport->negotiate.sign_info.free_signing_context) {
+ transport->negotiate.sign_info.free_signing_context(transport);
+ }
+
+ cli_null_set_signing(transport);
+}
+
+
+/**
+ * Sign a packet with the current mechanism
+ */
+void cli_request_calculate_sign_mac(struct cli_request *req)
+{
+ req->transport->negotiate.sign_info.sign_outgoing_message(req);
+}
+
+
+/**
+ * Check a packet with the current mechanism
+ * @return False if we had an established signing connection
+ * which had a back checksum, True otherwise
+ */
+BOOL cli_request_check_sign_mac(struct cli_request *req)
+{
+ BOOL good;
+
+ if (req->in.size < (HDR_SS_FIELD + 8)) {
+ good = False;
+ } else {
+ good = req->transport->negotiate.sign_info.check_incoming_message(req);
+ }
+
+ if (!good && req->transport->negotiate.sign_info.doing_signing) {
+ return False;
+ }
+
+ return True;
+}