diff options
Diffstat (limited to 'source4/libcli/raw')
-rw-r--r-- | source4/libcli/raw/README | 5 | ||||
-rw-r--r-- | source4/libcli/raw/clikrb5.c | 399 | ||||
-rw-r--r-- | source4/libcli/raw/clioplock.c | 57 | ||||
-rw-r--r-- | source4/libcli/raw/clirewrite.c | 22 | ||||
-rw-r--r-- | source4/libcli/raw/clisession.c | 444 | ||||
-rw-r--r-- | source4/libcli/raw/clisocket.c | 148 | ||||
-rw-r--r-- | source4/libcli/raw/clispnego.c | 533 | ||||
-rw-r--r-- | source4/libcli/raw/clitransport.c | 218 | ||||
-rw-r--r-- | source4/libcli/raw/clitree.c | 290 | ||||
-rw-r--r-- | source4/libcli/raw/raweas.c | 147 | ||||
-rw-r--r-- | source4/libcli/raw/rawfile.c | 687 | ||||
-rw-r--r-- | source4/libcli/raw/rawfileinfo.c | 527 | ||||
-rw-r--r-- | source4/libcli/raw/rawfsinfo.c | 282 | ||||
-rw-r--r-- | source4/libcli/raw/rawioctl.c | 118 | ||||
-rw-r--r-- | source4/libcli/raw/rawnegotiate.c | 157 | ||||
-rw-r--r-- | source4/libcli/raw/rawnotify.c | 116 | ||||
-rw-r--r-- | source4/libcli/raw/rawreadwrite.c | 321 | ||||
-rw-r--r-- | source4/libcli/raw/rawrequest.c | 1019 | ||||
-rw-r--r-- | source4/libcli/raw/rawsearch.c | 569 | ||||
-rw-r--r-- | source4/libcli/raw/rawsetfileinfo.c | 335 | ||||
-rw-r--r-- | source4/libcli/raw/rawtrans.c | 489 | ||||
-rw-r--r-- | source4/libcli/raw/smb_signing.c | 341 |
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; +} |