s3: Make cli_session_setup_nt1 async
[mimir/samba.git] / source3 / libsmb / cliconnect.c
index 86cd7e749aa87ab1ede1f580a15081609e9ea851..bdbc8f1e9d494039eb2587694867dd7eb9e5bc52 100644 (file)
@@ -524,18 +524,40 @@ static NTSTATUS cli_session_setup_plain(struct cli_state *cli,
    @param workgroup The user's domain.
 ****************************************************************************/
 
-static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user, 
-                                     const char *pass, size_t passlen,
-                                     const char *ntpass, size_t ntpasslen,
-                                     const char *workgroup)
+struct cli_session_setup_nt1_state {
+       struct cli_state *cli;
+       uint16_t vwv[13];
+       DATA_BLOB response;
+       DATA_BLOB session_key;
+       const char *user;
+};
+
+static void cli_session_setup_nt1_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_session_setup_nt1_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       struct cli_state *cli, const char *user,
+       const char *pass, size_t passlen,
+       const char *ntpass, size_t ntpasslen,
+       const char *workgroup)
 {
-       uint32 capabilities = cli_session_setup_capabilities(cli);
+       struct tevent_req *req, *subreq;
+       struct cli_session_setup_nt1_state *state;
        DATA_BLOB lm_response = data_blob_null;
        DATA_BLOB nt_response = data_blob_null;
        DATA_BLOB session_key = data_blob_null;
-       NTSTATUS result;
-       char *p;
-       bool ok;
+       uint16_t *vwv;
+       uint8_t *bytes;
+       char *workgroup_upper;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_session_setup_nt1_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->cli = cli;
+       state->user = user;
+       vwv = state->vwv;
 
        if (passlen == 0) {
                /* do nothing - guest login */
@@ -543,20 +565,35 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user,
                if (lp_client_ntlmv2_auth()) {
                        DATA_BLOB server_chal;
                        DATA_BLOB names_blob;
-                       server_chal = data_blob(cli->secblob.data, MIN(cli->secblob.length, 8)); 
 
-                       /* note that the 'workgroup' here is a best guess - we don't know
-                          the server's domain at this point.  The 'server name' is also
-                          dodgy... 
-                       */
-                       names_blob = NTLMv2_generate_names_blob(NULL, cli->called.name, workgroup);
+                       server_chal = data_blob(cli->secblob.data,
+                                               MIN(cli->secblob.length, 8));
+                       if (tevent_req_nomem(server_chal.data, req)) {
+                               return tevent_req_post(req, ev);
+                       }
+
+                       /*
+                        * note that the 'workgroup' here is a best
+                        * guess - we don't know the server's domain
+                        * at this point.  The 'server name' is also
+                        * dodgy...
+                        */
+                       names_blob = NTLMv2_generate_names_blob(
+                               NULL, cli->called.name, workgroup);
+
+                       if (tevent_req_nomem(names_blob.data, req)) {
+                               return tevent_req_post(req, ev);
+                       }
 
-                       if (!SMBNTLMv2encrypt(NULL, user, workgroup, pass, &server_chal, 
-                                             &names_blob,
-                                             &lm_response, &nt_response, NULL, &session_key)) {
+                       if (!SMBNTLMv2encrypt(NULL, user, workgroup, pass,
+                                             &server_chal, &names_blob,
+                                             &lm_response, &nt_response,
+                                             NULL, &session_key)) {
                                data_blob_free(&names_blob);
                                data_blob_free(&server_chal);
-                               return NT_STATUS_ACCESS_DENIED;
+                               tevent_req_nterror(
+                                       req, NT_STATUS_ACCESS_DENIED);
+                               return tevent_req_post(req, ev);
                        }
                        data_blob_free(&names_blob);
                        data_blob_free(&server_chal);
@@ -569,23 +606,50 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user,
                        nt_response = data_blob_null;
 #else
                        nt_response = data_blob(NULL, 24);
-                       SMBNTencrypt(pass,cli->secblob.data,nt_response.data);
+                       if (tevent_req_nomem(nt_response.data, req)) {
+                               return tevent_req_post(req, ev);
+                       }
+
+                       SMBNTencrypt(pass, cli->secblob.data,
+                                    nt_response.data);
 #endif
                        /* non encrypted password supplied. Ignore ntpass. */
                        if (lp_client_lanman_auth()) {
+
                                lm_response = data_blob(NULL, 24);
-                               if (!SMBencrypt(pass,cli->secblob.data, lm_response.data)) {
-                                       /* Oops, the LM response is invalid, just put 
-                                          the NT response there instead */
+                               if (tevent_req_nomem(lm_response.data, req)) {
+                                       return tevent_req_post(req, ev);
+                               }
+
+                               if (!SMBencrypt(pass,cli->secblob.data,
+                                               lm_response.data)) {
+                                       /*
+                                        * Oops, the LM response is
+                                        * invalid, just put the NT
+                                        * response there instead
+                                        */
                                        data_blob_free(&lm_response);
-                                       lm_response = data_blob(nt_response.data, nt_response.length);
+                                       lm_response = data_blob(
+                                               nt_response.data,
+                                               nt_response.length);
                                }
                        } else {
-                               /* LM disabled, place NT# in LM field instead */
-                               lm_response = data_blob(nt_response.data, nt_response.length);
+                               /*
+                                * LM disabled, place NT# in LM field
+                                * instead
+                                */
+                               lm_response = data_blob(
+                                       nt_response.data, nt_response.length);
+                       }
+
+                       if (tevent_req_nomem(lm_response.data, req)) {
+                               return tevent_req_post(req, ev);
                        }
 
                        session_key = data_blob(NULL, 16);
+                       if (tevent_req_nomem(session_key.data, req)) {
+                               return tevent_req_post(req, ev);
+                       }
 #ifdef LANMAN_ONLY
                        E_deshash(pass, session_key.data);
                        memset(&session_key.data[8], '\0', 8);
@@ -600,94 +664,189 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user,
                   signing because we don't have original key */
 
                lm_response = data_blob(pass, passlen);
+               if (tevent_req_nomem(lm_response.data, req)) {
+                       return tevent_req_post(req, ev);
+               }
+
                nt_response = data_blob(ntpass, ntpasslen);
+               if (tevent_req_nomem(nt_response.data, req)) {
+                       return tevent_req_post(req, ev);
+               }
        }
 
-       /* send a session setup command */
-       memset(cli->outbuf,'\0',smb_size);
+#ifdef LANMAN_ONLY
+       state->response = data_blob_talloc(
+               state, lm_response.data, lm_response.length);
+#else
+       state->response = data_blob_talloc(
+               state, nt_response.data, nt_response.length);
+#endif
+       if (tevent_req_nomem(state->response.data, req)) {
+               return tevent_req_post(req, ev);
+       }
 
-       cli_set_message(cli->outbuf,13,0,True);
-       SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
-       cli_setup_packet(cli);
+       if (session_key.data) {
+               state->session_key = data_blob_talloc(
+                       state, session_key.data, session_key.length);
+               if (tevent_req_nomem(state->session_key.data, req)) {
+                       return tevent_req_post(req, ev);
+               }
+       }
+       data_blob_free(&session_key);
 
-       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_vwv7,lm_response.length);
-       SSVAL(cli->outbuf,smb_vwv8,nt_response.length);
-       SIVAL(cli->outbuf,smb_vwv11,capabilities); 
-       p = smb_buf(cli->outbuf);
-       if (lm_response.length) {
-               memcpy(p,lm_response.data, lm_response.length); p += lm_response.length;
+       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, lm_response.length);
+       SSVAL(vwv+8, 0, nt_response.length);
+       SSVAL(vwv+9, 0, 0);
+       SSVAL(vwv+10, 0, 0);
+       SIVAL(vwv+11, 0, cli_session_setup_capabilities(cli));
+
+       bytes = talloc_array(state, uint8_t,
+                            lm_response.length + nt_response.length);
+       if (tevent_req_nomem(bytes, req)) {
+               return tevent_req_post(req, ev);
+       }
+       if (lm_response.length != 0) {
+               memcpy(bytes, lm_response.data, lm_response.length);
        }
-       if (nt_response.length) {
-               memcpy(p,nt_response.data, nt_response.length); p += nt_response.length;
+       if (nt_response.length != 0) {
+               memcpy(bytes + lm_response.length,
+                      nt_response.data, nt_response.length);
        }
-       p += clistr_push(cli, p, user, -1, STR_TERMINATE);
+       data_blob_free(&lm_response);
+       data_blob_free(&nt_response);
 
-       /* Upper case here might help some NTLMv2 implementations */
-       p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE|STR_UPPER);
-       p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
-       p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
-       cli_setup_bcc(cli, p);
+       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+                                  user, strlen(user)+1, NULL);
 
-       if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
-               result = cli_nt_error(cli);
-               goto end;
+       /*
+        * Upper case here might help some NTLMv2 implementations
+        */
+       workgroup_upper = talloc_strdup_upper(talloc_tos(), workgroup);
+       if (tevent_req_nomem(workgroup_upper, req)) {
+               return tevent_req_post(req, ev);
        }
+       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli),
+                                  workgroup_upper, strlen(workgroup_upper)+1,
+                                  NULL);
+       TALLOC_FREE(workgroup_upper);
 
-       /* show_msg(cli->inbuf); */
+       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 (cli_is_error(cli)) {
-               result = cli_nt_error(cli);
-               goto end;
+       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_nt1_done, req);
+       return req;
+}
 
-#ifdef LANMAN_ONLY
-       ok = cli_simple_set_signing(cli, session_key, lm_response);
-#else
-       ok = cli_simple_set_signing(cli, session_key, nt_response);
-#endif
-       if (ok) {
-               if (!cli_check_sign_mac(cli, cli->inbuf, 1)) {
-                       result = NT_STATUS_ACCESS_DENIED;
-                       goto end;
-               }
+static void cli_session_setup_nt1_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_session_setup_nt1_state *state = tevent_req_data(
+               req, struct cli_session_setup_nt1_state);
+       struct cli_state *cli = state->cli;
+       uint32_t num_bytes;
+       uint8_t *in;
+       char *inbuf;
+       uint8_t *bytes;
+       uint8_t *p;
+       NTSTATUS status;
+
+       status = cli_smb_recv(subreq, state, &in, 0, NULL, NULL,
+                             &num_bytes, &bytes);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
        }
 
-       /* use the returned vuid from now on */
-       cli->vuid = SVAL(cli->inbuf,smb_uid);
+       inbuf = (char *)in;
+       p = bytes;
+
+       cli->vuid = SVAL(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);
+       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);
 
        if (strstr(cli->server_type, "Samba")) {
                cli->is_samba = True;
        }
 
-       result = cli_set_username(cli, user);
-       if (!NT_STATUS_IS_OK(result)) {
-               goto end;
+       status = cli_set_username(cli, state->user);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
-
-       if (session_key.data) {
+       if (cli_simple_set_signing(cli, state->session_key, state->response)
+           && !cli_check_sign_mac(cli, (char *)in, 1)) {
+               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+               return;
+       }
+       if (state->session_key.data) {
                /* Have plaintext orginal */
-               cli_set_session_key(cli, session_key);
+               cli_set_session_key(cli, state->session_key);
        }
+       tevent_req_done(req);
+}
 
-       result = NT_STATUS_OK;
-end:   
-       data_blob_free(&lm_response);
-       data_blob_free(&nt_response);
-       data_blob_free(&session_key);
-       return result;
+static NTSTATUS cli_session_setup_nt1_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user,
+                                     const char *pass, size_t passlen,
+                                     const char *ntpass, size_t ntpasslen,
+                                     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_nt1_send(frame, ev, cli, user, pass, passlen,
+                                        ntpass, ntpasslen, workgroup);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_session_setup_nt1_recv(req);
+ fail:
+       TALLOC_FREE(frame);
+       if (!NT_STATUS_IS_OK(status)) {
+               cli_set_error(cli, status);
+       }
+       return status;
 }
 
 /* The following is calculated from :