X-Git-Url: http://git.samba.org/?p=samba.git;a=blobdiff_plain;f=source3%2Flibsmb%2Fcliconnect.c;h=fa79ebcea362606919f4dc409af0061758fdaa2d;hp=53a812d222f5a14acf3542ee6e3b0512b7a97636;hb=34f0cff0664f1c160ee7442461e9f875e8d8f4dc;hpb=3084d49cc000dbfeb01d931d737c695dcd392e4a diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index 53a812d222f..fa79ebcea36 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -3,22 +3,26 @@ client connect/disconnect routines Copyright (C) Andrew Tridgell 1994-1998 Copyright (C) Andrew Bartlett 2001-2003 - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" +#include "../libcli/auth/libcli_auth.h" +#include "../libcli/auth/spnego.h" +#include "smb_krb5.h" +#include "ntlmssp.h" static const struct { int prot; @@ -101,7 +105,7 @@ static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli, cli_set_message(cli->outbuf,10, 0, True); SCVAL(cli->outbuf,smb_com,SMBsesssetupX); cli_setup_packet(cli); - + SCVAL(cli->outbuf,smb_vwv0,0xFF); SSVAL(cli->outbuf,smb_vwv2,cli->max_xmit); SSVAL(cli->outbuf,smb_vwv3,2); @@ -127,7 +131,7 @@ static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli, if (cli_is_error(cli)) { return cli_nt_error(cli); } - + /* use the returned vuid from now on */ cli->vuid = SVAL(cli->inbuf,smb_uid); status = cli_set_username(cli, user); @@ -168,13 +172,15 @@ static uint32 cli_session_setup_capabilities(struct cli_state *cli) struct cli_session_setup_guest_state { struct cli_state *cli; uint16_t vwv[16]; + struct iovec bytes; }; static void cli_session_setup_guest_done(struct tevent_req *subreq); -struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli) +struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + struct tevent_req **psmbreq) { struct tevent_req *req, *subreq; struct cli_session_setup_guest_state *state; @@ -211,16 +217,42 @@ struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx, bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "Unix", 5, NULL); bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "Samba", 6, NULL); - if (tevent_req_nomem(bytes, req)) { - return tevent_req_post(req, ev); + if (bytes == NULL) { + TALLOC_FREE(req); + return NULL; } - subreq = cli_smb_send(state, ev, cli, SMBsesssetupX, 0, 13, vwv, - talloc_get_size(bytes), bytes); - if (tevent_req_nomem(subreq, req)) { - return tevent_req_post(req, ev); + state->bytes.iov_base = (void *)bytes; + state->bytes.iov_len = talloc_get_size(bytes); + + subreq = cli_smb_req_create(state, ev, cli, SMBsesssetupX, 0, 13, vwv, + 1, &state->bytes); + if (subreq == NULL) { + TALLOC_FREE(req); + return NULL; } tevent_req_set_callback(subreq, cli_session_setup_guest_done, req); + *psmbreq = subreq; + return req; +} + +struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli) +{ + struct tevent_req *req, *subreq; + NTSTATUS status; + + req = cli_session_setup_guest_create(mem_ctx, ev, cli, &subreq); + if (req == NULL) { + return NULL; + } + + status = cli_smb_req_send(subreq); + if (NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } return req; } @@ -328,14 +360,14 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli, char *p; NTSTATUS status; fstring lanman; - + fstr_sprintf( lanman, "Samba %s", samba_version_string()); memset(cli->outbuf, '\0', smb_size); cli_set_message(cli->outbuf,13,0,True); SCVAL(cli->outbuf,smb_com,SMBsesssetupX); cli_setup_packet(cli); - + SCVAL(cli->outbuf,smb_vwv0,0xFF); SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE); SSVAL(cli->outbuf,smb_vwv3,2); @@ -344,9 +376,9 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli, SSVAL(cli->outbuf,smb_vwv8,0); SIVAL(cli->outbuf,smb_vwv11,capabilities); p = smb_buf(cli->outbuf); - + /* check wether to send the ASCII or UNICODE version of the password */ - + if ( (capabilities & CAP_UNICODE) == 0 ) { p += clistr_push(cli, p, pass, -1, STR_TERMINATE); /* password */ SSVAL(cli->outbuf,smb_vwv7,PTR_DIFF(p, smb_buf(cli->outbuf))); @@ -362,7 +394,7 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli, p += clistr_push(cli, p, pass, -1, STR_UNICODE|STR_TERMINATE); /* unicode password */ SSVAL(cli->outbuf,smb_vwv8,PTR_DIFF(p, smb_buf(cli->outbuf))-1); } - + p += clistr_push(cli, p, user, -1, STR_TERMINATE); /* username */ p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE); /* workgroup */ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE); @@ -372,9 +404,9 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli, if (!cli_send_smb(cli) || !cli_receive_smb(cli)) { return cli_nt_error(cli); } - + show_msg(cli->inbuf); - + if (cli_is_error(cli)) { return cli_nt_error(cli); } @@ -433,11 +465,11 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user, the server's domain at this point. The 'server name' is also dodgy... */ - names_blob = NTLMv2_generate_names_blob(cli->called.name, workgroup); + names_blob = NTLMv2_generate_names_blob(NULL, cli->called.name, workgroup); - if (!SMBNTLMv2encrypt(user, workgroup, pass, &server_chal, + if (!SMBNTLMv2encrypt(NULL, user, workgroup, pass, &server_chal, &names_blob, - &lm_response, &nt_response, &session_key)) { + &lm_response, &nt_response, NULL, &session_key)) { data_blob_free(&names_blob); data_blob_free(&server_chal); return NT_STATUS_ACCESS_DENIED; @@ -474,7 +506,7 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user, E_deshash(pass, session_key.data); memset(&session_key.data[8], '\0', 8); #else - SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); + SMBsesskeygen_ntv1(nt_hash, session_key.data); #endif } cli_temp_set_signing(cli); @@ -493,7 +525,7 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user, cli_set_message(cli->outbuf,13,0,True); SCVAL(cli->outbuf,smb_com,SMBsesssetupX); cli_setup_packet(cli); - + SCVAL(cli->outbuf,smb_vwv0,0xFF); SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE); SSVAL(cli->outbuf,smb_vwv3,2); @@ -543,7 +575,7 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user, /* use the returned vuid from now on */ cli->vuid = SVAL(cli->inbuf,smb_uid); - + p = smb_buf(cli->inbuf); p += clistr_pull(cli->inbuf, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE); @@ -574,151 +606,212 @@ end: return result; } -/**************************************************************************** - Send a extended security session setup blob -****************************************************************************/ - -static bool cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob) -{ - uint32 capabilities = cli_session_setup_capabilities(cli); - char *p; - - capabilities |= CAP_EXTENDED_SECURITY; - - /* send a session setup command */ - memset(cli->outbuf,'\0',smb_size); +/* The following is calculated from : + * (smb_size-4) = 35 + * (smb_wcnt * 2) = 24 (smb_wcnt == 12 in cli_session_setup_blob_send() ) + * (strlen("Unix") + 1 + strlen("Samba") + 1) * 2 = 22 (unicode strings at + * end of packet. + */ - cli_set_message(cli->outbuf,12,0,True); - SCVAL(cli->outbuf,smb_com,SMBsesssetupX); +#define BASE_SESSSETUP_BLOB_PACKET_SIZE (35 + 24 + 22) - cli_setup_packet(cli); +struct cli_sesssetup_blob_state { + struct tevent_context *ev; + struct cli_state *cli; + DATA_BLOB blob; + uint16_t max_blob_size; + uint16_t vwv[12]; + uint8_t *buf; - SCVAL(cli->outbuf,smb_vwv0,0xFF); - SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE); - SSVAL(cli->outbuf,smb_vwv3,2); - SSVAL(cli->outbuf,smb_vwv4,1); - SIVAL(cli->outbuf,smb_vwv5,0); - SSVAL(cli->outbuf,smb_vwv7,blob.length); - SIVAL(cli->outbuf,smb_vwv10,capabilities); - p = smb_buf(cli->outbuf); - memcpy(p, blob.data, blob.length); - p += blob.length; - p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE); - p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE); - cli_setup_bcc(cli, p); - return cli_send_smb(cli); -} + NTSTATUS status; + char *inbuf; + DATA_BLOB ret_blob; +}; -/**************************************************************************** - Send a extended security session setup blob, returning a reply blob. -****************************************************************************/ +static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state, + struct tevent_req **psubreq); +static void cli_sesssetup_blob_done(struct tevent_req *subreq); -static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli) +static struct tevent_req *cli_sesssetup_blob_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + DATA_BLOB blob) { - DATA_BLOB blob2 = data_blob_null; - char *p; - size_t len; - - if (!cli_receive_smb(cli)) - return blob2; - - show_msg(cli->inbuf); + struct tevent_req *req, *subreq; + struct cli_sesssetup_blob_state *state; - if (cli_is_error(cli) && !NT_STATUS_EQUAL(cli_nt_error(cli), - NT_STATUS_MORE_PROCESSING_REQUIRED)) { - return blob2; + req = tevent_req_create(mem_ctx, &state, + struct cli_sesssetup_blob_state); + if (req == NULL) { + return NULL; } + state->ev = ev; + state->blob = blob; + state->cli = cli; - /* use the returned vuid from now on */ - cli->vuid = SVAL(cli->inbuf,smb_uid); - - p = smb_buf(cli->inbuf); - - blob2 = data_blob(p, SVAL(cli->inbuf, smb_vwv3)); + if (cli->max_xmit < BASE_SESSSETUP_BLOB_PACKET_SIZE + 1) { + DEBUG(1, ("cli_session_setup_blob: cli->max_xmit too small " + "(was %u, need minimum %u)\n", + (unsigned int)cli->max_xmit, + BASE_SESSSETUP_BLOB_PACKET_SIZE)); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + state->max_blob_size = + MIN(cli->max_xmit - BASE_SESSSETUP_BLOB_PACKET_SIZE, 0xFFFF); - p += blob2.length; - p += clistr_pull(cli->inbuf, cli->server_os, p, sizeof(fstring), - -1, STR_TERMINATE); + if (!cli_sesssetup_blob_next(state, &subreq)) { + tevent_req_nomem(NULL, req); + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_sesssetup_blob_done, req); + return req; +} - /* w2k with kerberos doesn't properly null terminate this field */ - len = smb_bufrem(cli->inbuf, p); - p += clistr_pull(cli->inbuf, cli->server_type, p, sizeof(fstring), - len, 0); +static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state, + struct tevent_req **psubreq) +{ + struct tevent_req *subreq; + uint16_t thistime; + + SCVAL(state->vwv+0, 0, 0xFF); + SCVAL(state->vwv+0, 1, 0); + SSVAL(state->vwv+1, 0, 0); + SSVAL(state->vwv+2, 0, CLI_BUFFER_SIZE); + SSVAL(state->vwv+3, 0, 2); + SSVAL(state->vwv+4, 0, 1); + SIVAL(state->vwv+5, 0, 0); + + thistime = MIN(state->blob.length, state->max_blob_size); + SSVAL(state->vwv+7, 0, thistime); + + SSVAL(state->vwv+8, 0, 0); + SSVAL(state->vwv+9, 0, 0); + SIVAL(state->vwv+10, 0, + cli_session_setup_capabilities(state->cli) + | CAP_EXTENDED_SECURITY); + + state->buf = (uint8_t *)talloc_memdup(state, state->blob.data, + thistime); + if (state->buf == NULL) { + return false; + } + state->blob.data += thistime; + state->blob.length -= thistime; - return blob2; + state->buf = smb_bytes_push_str(state->buf, cli_ucs2(state->cli), + "Unix", 5, NULL); + state->buf = smb_bytes_push_str(state->buf, cli_ucs2(state->cli), + "Samba", 6, NULL); + if (state->buf == NULL) { + return false; + } + subreq = cli_smb_send(state, state->ev, state->cli, SMBsesssetupX, 0, + 12, state->vwv, + talloc_get_size(state->buf), state->buf); + if (subreq == NULL) { + return false; + } + *psubreq = subreq; + return true; } -#ifdef HAVE_KRB5 -/**************************************************************************** - Send a extended security session setup blob, returning a reply blob. -****************************************************************************/ +static void cli_sesssetup_blob_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_sesssetup_blob_state *state = tevent_req_data( + req, struct cli_sesssetup_blob_state); + struct cli_state *cli = state->cli; + uint8_t wct; + uint16_t *vwv; + uint32_t num_bytes; + uint8_t *bytes; + NTSTATUS status; + uint8_t *p; + uint16_t blob_length; -/* The following is calculated from : - * (smb_size-4) = 35 - * (smb_wcnt * 2) = 24 (smb_wcnt == 12 in cli_session_setup_blob_send() ) - * (strlen("Unix") + 1 + strlen("Samba") + 1) * 2 = 22 (unicode strings at - * end of packet. - */ + status = cli_smb_recv(subreq, 1, &wct, &vwv, &num_bytes, &bytes); + if (!NT_STATUS_IS_OK(status) + && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); + return; + } -#define BASE_SESSSETUP_BLOB_PACKET_SIZE (35 + 24 + 22) + state->status = status; + TALLOC_FREE(state->buf); -static bool cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob) -{ - int32 remaining = blob.length; - int32 cur = 0; - DATA_BLOB send_blob = data_blob_null; - int32 max_blob_size = 0; - DATA_BLOB receive_blob = data_blob_null; + state->inbuf = (char *)cli_smb_inbuf(subreq); + cli->vuid = SVAL(state->inbuf, smb_uid); - if (cli->max_xmit < BASE_SESSSETUP_BLOB_PACKET_SIZE + 1) { - DEBUG(0,("cli_session_setup_blob: cli->max_xmit too small " - "(was %u, need minimum %u)\n", - (unsigned int)cli->max_xmit, - BASE_SESSSETUP_BLOB_PACKET_SIZE)); - cli_set_nt_error(cli, NT_STATUS_INVALID_PARAMETER); - return False; + blob_length = SVAL(vwv+3, 0); + if (blob_length > num_bytes) { + TALLOC_FREE(subreq); + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; } + state->ret_blob = data_blob_const(bytes, blob_length); - max_blob_size = cli->max_xmit - BASE_SESSSETUP_BLOB_PACKET_SIZE; + p = bytes + blob_length; - while ( remaining > 0) { - if (remaining >= max_blob_size) { - send_blob.length = max_blob_size; - remaining -= max_blob_size; - } else { - send_blob.length = remaining; - remaining = 0; - } - - send_blob.data = &blob.data[cur]; - cur += send_blob.length; + p += clistr_pull(state->inbuf, cli->server_os, + (char *)p, sizeof(fstring), + bytes+num_bytes-p, STR_TERMINATE); + p += clistr_pull(state->inbuf, cli->server_type, + (char *)p, sizeof(fstring), + bytes+num_bytes-p, STR_TERMINATE); + p += clistr_pull(state->inbuf, cli->server_domain, + (char *)p, sizeof(fstring), + bytes+num_bytes-p, STR_TERMINATE); - DEBUG(10, ("cli_session_setup_blob: Remaining (%u) sending (%u) current (%u)\n", - (unsigned int)remaining, - (unsigned int)send_blob.length, - (unsigned int)cur )); + if (strstr(cli->server_type, "Samba")) { + cli->is_samba = True; + } - if (!cli_session_setup_blob_send(cli, send_blob)) { - DEBUG(0, ("cli_session_setup_blob: send failed\n")); - return False; + if (state->blob.length != 0) { + TALLOC_FREE(subreq); + /* + * More to send + */ + if (!cli_sesssetup_blob_next(state, &subreq)) { + tevent_req_nomem(NULL, req); + return; } + tevent_req_set_callback(subreq, cli_sesssetup_blob_done, req); + return; + } + tevent_req_done(req); +} - receive_blob = cli_session_setup_blob_receive(cli); - data_blob_free(&receive_blob); +static NTSTATUS cli_sesssetup_blob_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *pblob, + char **pinbuf) +{ + struct cli_sesssetup_blob_state *state = tevent_req_data( + req, struct cli_sesssetup_blob_state); + NTSTATUS status; + char *inbuf; - if (cli_is_error(cli) && - !NT_STATUS_EQUAL( cli_get_nt_error(cli), - NT_STATUS_MORE_PROCESSING_REQUIRED)) { - DEBUG(0, ("cli_session_setup_blob: receive failed " - "(%s)\n", nt_errstr(cli_get_nt_error(cli)))); - cli->vuid = 0; - return False; - } + if (tevent_req_is_nterror(req, &status)) { + state->cli->vuid = 0; + return status; } - return True; + inbuf = talloc_move(mem_ctx, &state->inbuf); + if (pblob != NULL) { + *pblob = state->ret_blob; + } + if (pinbuf != NULL) { + *pinbuf = inbuf; + } + /* could be NT_STATUS_MORE_PROCESSING_REQUIRED */ + return state->status; } +#ifdef HAVE_KRB5 + /**************************************************************************** Use in-memory credentials cache ****************************************************************************/ @@ -731,185 +824,358 @@ static void use_in_memory_ccache(void) { Do a spnego/kerberos encrypted session setup. ****************************************************************************/ -static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char *principal, const char *workgroup) -{ +struct cli_session_setup_kerberos_state { + struct cli_state *cli; DATA_BLOB negTokenTarg; DATA_BLOB session_key_krb5; - NTSTATUS nt_status; - int rc; + ADS_STATUS ads_status; +}; - cli_temp_set_signing(cli); +static void cli_session_setup_kerberos_done(struct tevent_req *subreq); + +static struct tevent_req *cli_session_setup_kerberos_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, + const char *principal, const char *workgroup) +{ + struct tevent_req *req, *subreq; + struct cli_session_setup_kerberos_state *state; + int rc; DEBUG(2,("Doing kerberos session setup\n")); - /* generate the encapsulated kerberos5 ticket */ - rc = spnego_gen_negTokenTarg(principal, 0, &negTokenTarg, &session_key_krb5, 0, NULL); + req = tevent_req_create(mem_ctx, &state, + struct cli_session_setup_kerberos_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + state->ads_status = ADS_SUCCESS; + + cli_temp_set_signing(cli); + /* + * Ok, this is cheated: spnego_gen_negTokenTarg can block if + * we have to acquire a ticket. To be fixed later :-) + */ + rc = spnego_gen_negTokenTarg(principal, 0, &state->negTokenTarg, + &state->session_key_krb5, 0, NULL); if (rc) { - DEBUG(1, ("cli_session_setup_kerberos: spnego_gen_negTokenTarg failed: %s\n", - error_message(rc))); - return ADS_ERROR_KRB5(rc); + DEBUG(1, ("cli_session_setup_kerberos: " + "spnego_gen_negTokenTarg failed: %s\n", + error_message(rc))); + state->ads_status = ADS_ERROR_KRB5(rc); + tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL); + return tevent_req_post(req, ev); } #if 0 - file_save("negTokenTarg.dat", negTokenTarg.data, negTokenTarg.length); + file_save("negTokenTarg.dat", state->negTokenTarg.data, + state->negTokenTarg.length); #endif - if (!cli_session_setup_blob(cli, negTokenTarg)) { - nt_status = cli_nt_error(cli); - goto nt_error; + subreq = cli_sesssetup_blob_send(state, ev, cli, state->negTokenTarg); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); } + tevent_req_set_callback(subreq, cli_session_setup_kerberos_done, req); + return req; +} - if (cli_is_error(cli)) { - nt_status = cli_nt_error(cli); - if (NT_STATUS_IS_OK(nt_status)) { - nt_status = NT_STATUS_UNSUCCESSFUL; - } - goto nt_error; - } +static void cli_session_setup_kerberos_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_session_setup_kerberos_state *state = tevent_req_data( + req, struct cli_session_setup_kerberos_state); + char *inbuf = NULL; + NTSTATUS status; - cli_set_session_key(cli, session_key_krb5); + status = cli_sesssetup_blob_recv(subreq, talloc_tos(), NULL, &inbuf); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); + return; + } - if (cli_simple_set_signing( - cli, session_key_krb5, data_blob_null)) { + cli_set_session_key(state->cli, state->session_key_krb5); - if (!cli_check_sign_mac(cli, cli->inbuf, 1)) { - nt_status = NT_STATUS_ACCESS_DENIED; - goto nt_error; - } + if (cli_simple_set_signing(state->cli, state->session_key_krb5, + data_blob_null) + && !cli_check_sign_mac(state->cli, inbuf, 1)) { + TALLOC_FREE(subreq); + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return; } + TALLOC_FREE(subreq); + tevent_req_done(req); +} - data_blob_free(&negTokenTarg); - data_blob_free(&session_key_krb5); +static ADS_STATUS cli_session_setup_kerberos_recv(struct tevent_req *req) +{ + struct cli_session_setup_kerberos_state *state = tevent_req_data( + req, struct cli_session_setup_kerberos_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return ADS_ERROR_NT(status); + } + return state->ads_status; +} - return ADS_ERROR_NT(NT_STATUS_OK); +static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, + const char *principal, + const char *workgroup) +{ + struct tevent_context *ev; + struct tevent_req *req; + ADS_STATUS status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); -nt_error: - data_blob_free(&negTokenTarg); - data_blob_free(&session_key_krb5); - cli->vuid = 0; - return ADS_ERROR_NT(nt_status); + if (cli_has_async_calls(cli)) { + return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + ev = tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_session_setup_kerberos_send(ev, ev, cli, principal, + workgroup); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll(req, ev)) { + status = ADS_ERROR_SYSTEM(errno); + goto fail; + } + status = cli_session_setup_kerberos_recv(req); +fail: + TALLOC_FREE(ev); + return status; } #endif /* HAVE_KRB5 */ - /**************************************************************************** Do a spnego/NTLMSSP encrypted session setup. ****************************************************************************/ -static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *user, - const char *pass, const char *domain) -{ +struct cli_session_setup_ntlmssp_state { + struct tevent_context *ev; + struct cli_state *cli; struct ntlmssp_state *ntlmssp_state; - NTSTATUS nt_status; - int turn = 1; - DATA_BLOB msg1; - DATA_BLOB blob = data_blob_null; - DATA_BLOB blob_in = data_blob_null; - DATA_BLOB blob_out = data_blob_null; + int turn; + DATA_BLOB blob_out; +}; - cli_temp_set_signing(cli); +static int cli_session_setup_ntlmssp_state_destructor( + struct cli_session_setup_ntlmssp_state *state) +{ + if (state->ntlmssp_state != NULL) { + ntlmssp_end(&state->ntlmssp_state); + } + return 0; +} - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) { - return nt_status; +static void cli_session_setup_ntlmssp_done(struct tevent_req *req); + +static struct tevent_req *cli_session_setup_ntlmssp_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, + const char *user, const char *pass, const char *domain) +{ + struct tevent_req *req, *subreq; + struct cli_session_setup_ntlmssp_state *state; + NTSTATUS status; + DATA_BLOB blob_out; + + req = tevent_req_create(mem_ctx, &state, + struct cli_session_setup_ntlmssp_state); + if (req == NULL) { + return NULL; } - ntlmssp_want_feature(ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY); + state->ev = ev; + state->cli = cli; + state->turn = 1; - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) { - return nt_status; + state->ntlmssp_state = NULL; + talloc_set_destructor( + state, cli_session_setup_ntlmssp_state_destructor); + + cli_temp_set_signing(cli); + + status = ntlmssp_client_start(&state->ntlmssp_state); + if (!NT_STATUS_IS_OK(status)) { + goto fail; } - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) { - return nt_status; + ntlmssp_want_feature(state->ntlmssp_state, + NTLMSSP_FEATURE_SESSION_KEY); + status = ntlmssp_set_username(state->ntlmssp_state, user); + if (!NT_STATUS_IS_OK(status)) { + goto fail; } - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, pass))) { - return nt_status; + status = ntlmssp_set_domain(state->ntlmssp_state, domain); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + status = ntlmssp_set_password(state->ntlmssp_state, pass); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + status = ntlmssp_update(state->ntlmssp_state, data_blob_null, + &blob_out); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + goto fail; } - do { - nt_status = ntlmssp_update(ntlmssp_state, - blob_in, &blob_out); - data_blob_free(&blob_in); - if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) || NT_STATUS_IS_OK(nt_status)) { - if (turn == 1) { - /* and wrap it in a SPNEGO wrapper */ - msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out); - } else { - /* wrap it in SPNEGO */ - msg1 = spnego_gen_auth(blob_out); - } + state->blob_out = gen_negTokenInit(OID_NTLMSSP, blob_out); + data_blob_free(&blob_out); - /* now send that blob on its way */ - if (!cli_session_setup_blob_send(cli, msg1)) { - DEBUG(3, ("Failed to send NTLMSSP/SPNEGO blob to server!\n")); - nt_status = NT_STATUS_UNSUCCESSFUL; - } else { - blob = cli_session_setup_blob_receive(cli); - - nt_status = cli_nt_error(cli); - if (cli_is_error(cli) && NT_STATUS_IS_OK(nt_status)) { - if (cli->smb_rw_error == SMB_READ_BAD_SIG) { - nt_status = NT_STATUS_ACCESS_DENIED; - } else { - nt_status = NT_STATUS_UNSUCCESSFUL; - } - } - } - data_blob_free(&msg1); + subreq = cli_sesssetup_blob_send(state, ev, cli, state->blob_out); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_session_setup_ntlmssp_done, req); + return req; +fail: + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); +} + +static void cli_session_setup_ntlmssp_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_session_setup_ntlmssp_state *state = tevent_req_data( + req, struct cli_session_setup_ntlmssp_state); + DATA_BLOB blob_in, msg_in, blob_out; + char *inbuf = NULL; + bool parse_ret; + NTSTATUS status; + + status = cli_sesssetup_blob_recv(subreq, talloc_tos(), &blob_in, + &inbuf); + TALLOC_FREE(subreq); + data_blob_free(&state->blob_out); + + if (NT_STATUS_IS_OK(status)) { + if (state->cli->server_domain[0] == '\0') { + fstrcpy(state->cli->server_domain, + state->ntlmssp_state->server_domain); } + cli_set_session_key( + state->cli, state->ntlmssp_state->session_key); - if (!blob.length) { - if (NT_STATUS_IS_OK(nt_status)) { - nt_status = NT_STATUS_UNSUCCESSFUL; - } - } else if ((turn == 1) && - NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - DATA_BLOB tmp_blob = data_blob_null; - /* the server might give us back two challenges */ - if (!spnego_parse_challenge(blob, &blob_in, - &tmp_blob)) { - DEBUG(3,("Failed to parse challenges\n")); - nt_status = NT_STATUS_INVALID_PARAMETER; - } - data_blob_free(&tmp_blob); - } else { - if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP, - &blob_in)) { - DEBUG(3,("Failed to parse auth response\n")); - if (NT_STATUS_IS_OK(nt_status) - || NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) - nt_status = NT_STATUS_INVALID_PARAMETER; - } + if (cli_simple_set_signing( + state->cli, state->ntlmssp_state->session_key, + data_blob_null) + && !cli_check_sign_mac(state->cli, inbuf, 1)) { + TALLOC_FREE(subreq); + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return; } - data_blob_free(&blob); - data_blob_free(&blob_out); - turn++; - } while (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)); + TALLOC_FREE(subreq); + ntlmssp_end(&state->ntlmssp_state); + tevent_req_done(req); + return; + } + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + tevent_req_nterror(req, status); + return; + } - data_blob_free(&blob_in); + if (blob_in.length == 0) { + tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL); + return; + } - if (NT_STATUS_IS_OK(nt_status)) { + if ((state->turn == 1) + && NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + DATA_BLOB tmp_blob = data_blob_null; + /* the server might give us back two challenges */ + parse_ret = spnego_parse_challenge(blob_in, &msg_in, + &tmp_blob); + data_blob_free(&tmp_blob); + } else { + parse_ret = spnego_parse_auth_response(blob_in, status, + OID_NTLMSSP, &msg_in); + } + state->turn += 1; + + if (!parse_ret) { + DEBUG(3,("Failed to parse auth response\n")); + if (NT_STATUS_IS_OK(status) + || NT_STATUS_EQUAL(status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + tevent_req_nterror( + req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + } - fstrcpy(cli->server_domain, ntlmssp_state->server_domain); - cli_set_session_key(cli, ntlmssp_state->session_key); + status = ntlmssp_update(state->ntlmssp_state, msg_in, &blob_out); - if (cli_simple_set_signing( - cli, ntlmssp_state->session_key, data_blob_null)) { + if (!NT_STATUS_IS_OK(status) + && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + TALLOC_FREE(subreq); + ntlmssp_end(&state->ntlmssp_state); + tevent_req_nterror(req, status); + return; + } - if (!cli_check_sign_mac(cli, cli->inbuf, 1)) { - nt_status = NT_STATUS_ACCESS_DENIED; - } - } + state->blob_out = spnego_gen_auth(blob_out); + TALLOC_FREE(subreq); + if (tevent_req_nomem(state->blob_out.data, req)) { + return; } - /* we have a reference conter on ntlmssp_state, if we are signing - then the state will be kept by the signing engine */ + subreq = cli_sesssetup_blob_send(state, state->ev, state->cli, + state->blob_out); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_session_setup_ntlmssp_done, req); +} - ntlmssp_end(&ntlmssp_state); +static NTSTATUS cli_session_setup_ntlmssp_recv(struct tevent_req *req) +{ + struct cli_session_setup_ntlmssp_state *state = tevent_req_data( + req, struct cli_session_setup_ntlmssp_state); + NTSTATUS status; - if (!NT_STATUS_IS_OK(nt_status)) { - cli->vuid = 0; + if (tevent_req_is_nterror(req, &status)) { + state->cli->vuid = 0; + return status; + } + return NT_STATUS_OK; +} + +static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, + const char *user, + const char *pass, + const char *domain) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (cli_has_async_calls(cli)) { + return NT_STATUS_INVALID_PARAMETER; + } + ev = tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_session_setup_ntlmssp_send(ev, ev, cli, user, pass, domain); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_session_setup_ntlmssp_recv(req); +fail: + TALLOC_FREE(ev); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); } - return nt_status; + return status; } /**************************************************************************** @@ -958,7 +1224,10 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user, /* make sure the server understands kerberos */ for (i=0;OIDs[i];i++) { - DEBUG(3,("got OID=%s\n", OIDs[i])); + if (i == 0) + DEBUG(3,("got OID=%s\n", OIDs[i])); + else + DEBUGADD(3,("got OID=%s\n", OIDs[i])); if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 || strcmp(OIDs[i], OID_KERBEROS5) == 0) { cli->got_kerberos_mechanism = True; @@ -1194,25 +1463,88 @@ NTSTATUS cli_session_setup(struct cli_state *cli, Send a uloggoff. *****************************************************************************/ -bool cli_ulogoff(struct cli_state *cli) +struct cli_ulogoff_state { + struct cli_state *cli; + uint16_t vwv[2]; +}; + +static void cli_ulogoff_done(struct tevent_req *subreq); + +struct tevent_req *cli_ulogoff_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli) { - memset(cli->outbuf,'\0',smb_size); - cli_set_message(cli->outbuf,2,0,True); - SCVAL(cli->outbuf,smb_com,SMBulogoffX); - cli_setup_packet(cli); - SSVAL(cli->outbuf,smb_vwv0,0xFF); - SSVAL(cli->outbuf,smb_vwv2,0); /* no additional info */ + struct tevent_req *req, *subreq; + struct cli_ulogoff_state *state; - cli_send_smb(cli); - if (!cli_receive_smb(cli)) - return False; + req = tevent_req_create(mem_ctx, &state, struct cli_ulogoff_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; - if (cli_is_error(cli)) { - return False; + SCVAL(state->vwv+0, 0, 0xFF); + SCVAL(state->vwv+1, 0, 0); + SSVAL(state->vwv+2, 0, 0); + + subreq = cli_smb_send(state, ev, cli, SMBulogoffX, 0, 2, state->vwv, + 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); } + tevent_req_set_callback(subreq, cli_ulogoff_done, req); + return req; +} + +static void cli_ulogoff_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_ulogoff_state *state = tevent_req_data( + req, struct cli_ulogoff_state); + NTSTATUS status; - cli->cnum = -1; - return True; + status = cli_smb_recv(subreq, 0, NULL, NULL, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + state->cli->vuid = -1; + tevent_req_done(req); +} + +NTSTATUS cli_ulogoff_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_ulogoff(struct cli_state *cli) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (cli_has_async_calls(cli)) { + return NT_STATUS_INVALID_PARAMETER; + } + ev = tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_ulogoff_send(ev, ev, cli); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_ulogoff_recv(req); +fail: + TALLOC_FREE(ev); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } + return status; } /**************************************************************************** @@ -1222,15 +1554,17 @@ bool cli_ulogoff(struct cli_state *cli) struct cli_tcon_andx_state { struct cli_state *cli; uint16_t vwv[4]; + struct iovec bytes; }; static void cli_tcon_andx_done(struct tevent_req *subreq); -struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, - const char *share, const char *dev, - const char *pass, int passlen) +struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + const char *share, const char *dev, + const char *pass, int passlen, + struct tevent_req **psmbreq) { struct tevent_req *req, *subreq; struct cli_tcon_andx_state *state; @@ -1239,6 +1573,8 @@ struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, char *tmp = NULL; uint8_t *bytes; + *psmbreq = NULL; + req = tevent_req_create(mem_ctx, &state, struct cli_tcon_andx_state); if (req == NULL) { return NULL; @@ -1318,8 +1654,9 @@ struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, */ tmp = talloc_asprintf_strupper_m(talloc_tos(), "\\\\%s\\%s", cli->desthost, share); - if (tevent_req_nomem(tmp, req)) { - return tevent_req_post(req, ev); + if (tmp == NULL) { + TALLOC_FREE(req); + return NULL; } bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), tmp, strlen(tmp)+1, NULL); @@ -1329,22 +1666,29 @@ struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, * Add the devicetype */ tmp = talloc_strdup_upper(talloc_tos(), dev); - if (tevent_req_nomem(tmp, req)) { - return tevent_req_post(req, ev); + if (tmp == NULL) { + TALLOC_FREE(req); + return NULL; } bytes = smb_bytes_push_str(bytes, false, tmp, strlen(tmp)+1, NULL); TALLOC_FREE(tmp); - if (tevent_req_nomem(bytes, req)) { - return tevent_req_post(req, ev); + if (bytes == NULL) { + TALLOC_FREE(req); + return NULL; } - subreq = cli_smb_send(state, ev, cli, SMBtconX, 0, 4, vwv, - talloc_get_size(bytes), bytes); - if (tevent_req_nomem(subreq, req)) { - return tevent_req_post(req, ev); + state->bytes.iov_base = (void *)bytes; + state->bytes.iov_len = talloc_get_size(bytes); + + subreq = cli_smb_req_create(state, ev, cli, SMBtconX, 0, 4, vwv, + 1, &state->bytes); + if (subreq == NULL) { + TALLOC_FREE(req); + return NULL; } tevent_req_set_callback(subreq, cli_tcon_andx_done, req); + *psmbreq = subreq; return req; access_denied: @@ -1352,6 +1696,31 @@ struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, return tevent_req_post(req, ev); } +struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + const char *share, const char *dev, + const char *pass, int passlen) +{ + struct tevent_req *req, *subreq; + NTSTATUS status; + + req = cli_tcon_andx_create(mem_ctx, ev, cli, share, dev, pass, passlen, + &subreq); + if (req == NULL) { + return NULL; + } + if (subreq == NULL) { + return req; + } + status = cli_smb_req_send(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } + return req; +} + static void cli_tcon_andx_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data( @@ -1447,24 +1816,83 @@ NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share, Send a tree disconnect. ****************************************************************************/ -bool cli_tdis(struct cli_state *cli) +struct cli_tdis_state { + struct cli_state *cli; +}; + +static void cli_tdis_done(struct tevent_req *subreq); + +struct tevent_req *cli_tdis_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli) { - memset(cli->outbuf,'\0',smb_size); - cli_set_message(cli->outbuf,0,0,True); - SCVAL(cli->outbuf,smb_com,SMBtdis); - SSVAL(cli->outbuf,smb_tid,cli->cnum); - cli_setup_packet(cli); + struct tevent_req *req, *subreq; + struct cli_tdis_state *state; - cli_send_smb(cli); - if (!cli_receive_smb(cli)) - return False; + req = tevent_req_create(mem_ctx, &state, struct cli_tdis_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; - if (cli_is_error(cli)) { - return False; + subreq = cli_smb_send(state, ev, cli, SMBtdis, 0, 0, NULL, 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); } + tevent_req_set_callback(subreq, cli_tdis_done, req); + return req; +} - cli->cnum = -1; - return True; +static void cli_tdis_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_tdis_state *state = tevent_req_data( + req, struct cli_tdis_state); + NTSTATUS status; + + status = cli_smb_recv(subreq, 0, NULL, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + state->cli->cnum = -1; + tevent_req_done(req); +} + +NTSTATUS cli_tdis_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_tdis(struct cli_state *cli) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (cli_has_async_calls(cli)) { + return NT_STATUS_INVALID_PARAMETER; + } + ev = tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_tdis_send(ev, ev, cli); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_tdis_recv(req); +fail: + TALLOC_FREE(ev); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } + return status; } /**************************************************************************** @@ -1520,6 +1948,7 @@ struct tevent_req *cli_negprot_send(TALLOC_CTX *mem_ctx, struct cli_negprot_state *state; uint8_t *bytes = NULL; int numprots; + uint16_t cnum; req = tevent_req_create(mem_ctx, &state, struct cli_negprot_state); if (req == NULL) { @@ -1550,8 +1979,13 @@ struct tevent_req *cli_negprot_send(TALLOC_CTX *mem_ctx, } } + cnum = cli->cnum; + + cli->cnum = 0; subreq = cli_smb_send(state, ev, cli, SMBnegprot, 0, 0, NULL, talloc_get_size(bytes), bytes); + cli->cnum = cnum; + if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } @@ -1576,6 +2010,7 @@ static void cli_negprot_done(struct tevent_req *subreq) status = cli_smb_recv(subreq, 1, &wct, &vwv, &num_bytes, &bytes); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(subreq); + tevent_req_nterror(req, status); return; }