summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/libsmb/cliconnect.c368
1 files changed, 285 insertions, 83 deletions
diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
index dccc91425c..ecd0bd8980 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -701,11 +701,6 @@ static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
return blob2;
}
-#ifdef HAVE_KRB5
-/****************************************************************************
- Send a extended security session setup blob, returning a reply blob.
-****************************************************************************/
-
/* The following is calculated from :
* (smb_size-4) = 35
* (smb_wcnt * 2) = 24 (smb_wcnt == 12 in cli_session_setup_blob_send() )
@@ -715,63 +710,203 @@ static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
#define BASE_SESSSETUP_BLOB_PACKET_SIZE (35 + 24 + 22)
-static bool cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob)
+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;
+
+ NTSTATUS status;
+ char *inbuf;
+ DATA_BLOB ret_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 struct tevent_req *cli_sesssetup_blob_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ 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;
+ struct tevent_req *req, *subreq;
+ struct cli_sesssetup_blob_state *state;
+
+ 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;
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;
+ 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);
- max_blob_size = cli->max_xmit - BASE_SESSSETUP_BLOB_PACKET_SIZE;
+ 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;
+}
- 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;
- }
+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;
+
+ 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;
+}
- send_blob.data = &blob.data[cur];
- cur += send_blob.length;
+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;
- DEBUG(10, ("cli_session_setup_blob: Remaining (%u) sending (%u) current (%u)\n",
- (unsigned int)remaining,
- (unsigned int)send_blob.length,
- (unsigned int)cur ));
+ 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;
+ }
- if (!cli_session_setup_blob_send(cli, send_blob)) {
- DEBUG(0, ("cli_session_setup_blob: send failed\n"));
- return False;
- }
+ state->status = status;
+ TALLOC_FREE(state->buf);
- receive_blob = cli_session_setup_blob_receive(cli);
- data_blob_free(&receive_blob);
+ state->inbuf = (char *)cli_smb_inbuf(subreq);
+ cli->vuid = SVAL(state->inbuf, smb_uid);
- 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;
+ 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);
+
+ p = bytes + 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);
+
+ if (strstr(cli->server_type, "Samba")) {
+ cli->is_samba = True;
+ }
+
+ 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);
+}
- return True;
+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 (tevent_req_is_nterror(req, &status)) {
+ state->cli->vuid = 0;
+ return status;
+ }
+
+ 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
****************************************************************************/
@@ -784,68 +919,135 @@ 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;
- return ADS_ERROR_NT(NT_STATUS_OK);
+ if (tevent_req_is_nterror(req, &status)) {
+ return ADS_ERROR_NT(status);
+ }
+ return state->ads_status;
+}
-nt_error:
- data_blob_free(&negTokenTarg);
- data_blob_free(&session_key_krb5);
- cli->vuid = 0;
- return ADS_ERROR_NT(nt_status);
+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);
+
+ 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.
****************************************************************************/