s3: Make plaintext session setup async
authorVolker Lendecke <vl@samba.org>
Sat, 2 Apr 2011 21:54:07 +0000 (23:54 +0200)
committerJeremy Allison <jra@samba.org>
Tue, 26 Apr 2011 19:22:55 +0000 (12:22 -0700)
Signed-off-by: Jeremy Allison <jra@samba.org>
source3/libsmb/cliconnect.c

index e3d97b60fa7c656819824d1a5b2939a772223fb3..86cd7e749aa87ab1ede1f580a15081609e9ea851 100644 (file)
@@ -350,82 +350,168 @@ static NTSTATUS cli_session_setup_guest(struct cli_state *cli)
  Do a NT1 plaintext session setup.
 ****************************************************************************/
 
-static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli,
-                                           const char *user, const char *pass,
-                                           const char *workgroup)
-{
-       uint32 capabilities = cli_session_setup_capabilities(cli);
-       char *p;
-       NTSTATUS status;
-       fstring lanman;
+struct cli_session_setup_plain_state {
+       struct cli_state *cli;
+       uint16_t vwv[13];
+       const char *user;
+};
 
-       fstr_sprintf( lanman, "Samba %s", samba_version_string());
+static void cli_session_setup_plain_done(struct tevent_req *subreq);
 
-       memset(cli->outbuf, '\0', smb_size);
-       cli_set_message(cli->outbuf,13,0,True);
-       SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
-       cli_setup_packet(cli);
+static struct tevent_req *cli_session_setup_plain_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       struct cli_state *cli,
+       const char *user, const char *pass, const char *workgroup)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_session_setup_plain_state *state;
+       uint16_t *vwv;
+       uint8_t *bytes;
+       size_t passlen;
+       char *version;
 
-       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,cli->pid);
-       SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
-       SSVAL(cli->outbuf,smb_vwv8,0);
-       SIVAL(cli->outbuf,smb_vwv11,capabilities); 
-       p = smb_buf(cli->outbuf);
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_session_setup_plain_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->cli = cli;
+       state->user = user;
+       vwv = state->vwv;
 
-       /* check wether to send the ASCII or UNICODE version of the password */
+       SCVAL(vwv+0, 0, 0xff);
+       SCVAL(vwv+0, 1, 0);
+       SSVAL(vwv+1, 0, 0);
+       SSVAL(vwv+2, 0, CLI_BUFFER_SIZE);
+       SSVAL(vwv+3, 0, 2);
+       SSVAL(vwv+4, 0, cli->pid);
+       SIVAL(vwv+5, 0, cli->sesskey);
+       SSVAL(vwv+7, 0, 0);
+       SSVAL(vwv+8, 0, 0);
+       SSVAL(vwv+9, 0, 0);
+       SSVAL(vwv+10, 0, 0);
+       SIVAL(vwv+11, 0, cli_session_setup_capabilities(cli));
 
-       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)));
+       bytes = talloc_array(state, uint8_t, 0);
+       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), pass, strlen(pass)+1,
+                                  &passlen);
+       if (tevent_req_nomem(bytes, req)) {
+               return tevent_req_post(req, ev);
        }
-       else {
-               /* For ucs2 passwords clistr_push calls ucs2_align, which causes
-                * the space taken by the unicode password to be one byte too
-                * long (as we're on an odd byte boundary here). Reduce the
-                * count by 1 to cope with this. Fixes smbclient against NetApp
-                * servers which can't cope. Fix from
-                * bryan.kolodziej@allenlund.com in bug #3840.
-                */
-               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);        
+       SSVAL(vwv + (cli_ucs2(cli) ? 8 : 7), 0, passlen);
+
+       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+                                  user, strlen(user)+1, NULL);
+       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+                                  workgroup, strlen(workgroup)+1, NULL);
+       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+                                  "Unix", 5, NULL);
+
+       version = talloc_asprintf(talloc_tos(), "Samba %s",
+                                 samba_version_string());
+       if (tevent_req_nomem(version, req)){
+               return tevent_req_post(req, ev);
        }
+       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+                                  version, strlen(version)+1, NULL);
+       TALLOC_FREE(version);
 
-       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);
-       p += clistr_push(cli, p, lanman, -1, STR_TERMINATE);
-       cli_setup_bcc(cli, p);
+       if (tevent_req_nomem(bytes, req)) {
+               return tevent_req_post(req, ev);
+       }
 
-       if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
-               return cli_nt_error(cli);
+       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);
        }
+       tevent_req_set_callback(subreq, cli_session_setup_plain_done, req);
+       return req;
+}
 
-       show_msg(cli->inbuf);
+static void cli_session_setup_plain_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_session_setup_plain_state *state = tevent_req_data(
+               req, struct cli_session_setup_plain_state);
+       struct cli_state *cli = state->cli;
+       uint32_t num_bytes;
+       uint8_t *in;
+       char *inbuf;
+       uint8_t *bytes;
+       uint8_t *p;
+       NTSTATUS status;
 
-       if (cli_is_error(cli)) {
-               return cli_nt_error(cli);
+       status = cli_smb_recv(subreq, state, &in, 0, NULL, NULL,
+                             &num_bytes, &bytes);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
 
-       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);
-       p += clistr_pull(cli->inbuf, cli->server_type, p, sizeof(fstring),
-                        -1, STR_TERMINATE);
-       p += clistr_pull(cli->inbuf, cli->server_domain, p, sizeof(fstring),
-                        -1, STR_TERMINATE);
-       status = cli_set_username(cli, user);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       inbuf = (char *)in;
+       p = bytes;
+
+       cli->vuid = SVAL(inbuf, smb_uid);
+
+       p += clistr_pull(inbuf, cli->server_os, (char *)p, sizeof(fstring),
+                        bytes+num_bytes-p, STR_TERMINATE);
+       p += clistr_pull(inbuf, cli->server_type, (char *)p, sizeof(fstring),
+                        bytes+num_bytes-p, STR_TERMINATE);
+       p += clistr_pull(inbuf, cli->server_domain, (char *)p, sizeof(fstring),
+                        bytes+num_bytes-p, STR_TERMINATE);
+
+       status = cli_set_username(cli, state->user);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
        if (strstr(cli->server_type, "Samba")) {
                cli->is_samba = True;
        }
+       tevent_req_done(req);
+}
 
-       return NT_STATUS_OK;
+static NTSTATUS cli_session_setup_plain_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+static NTSTATUS cli_session_setup_plain(struct cli_state *cli,
+                                       const char *user, const char *pass,
+                                       const char *workgroup)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct event_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       if (cli_has_async_calls(cli)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+       ev = event_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cli_session_setup_plain_send(frame, ev, cli, user, pass,
+                                          workgroup);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_session_setup_plain_recv(req);
+ fail:
+       TALLOC_FREE(frame);
+       if (!NT_STATUS_IS_OK(status)) {
+               cli_set_error(cli, status);
+       }
+       return status;
 }
 
 /****************************************************************************
@@ -1420,7 +1506,7 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
            connect */
 
        if ((cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) == 0) 
-               return cli_session_setup_plaintext(cli, user, "", workgroup);
+               return cli_session_setup_plain(cli, user, "", workgroup);
 
        /* if the server doesn't support encryption then we have to use 
           plaintext. The second password is ignored */
@@ -1431,7 +1517,7 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
                                  " or 'client ntlmv2 auth = yes'\n"));
                        return NT_STATUS_ACCESS_DENIED;
                }
-               return cli_session_setup_plaintext(cli, user, pass, workgroup);
+               return cli_session_setup_plain(cli, user, pass, workgroup);
        }
 
        /* if the server supports extended security then use SPNEGO */