#include "includes.h"
#include "libsmb/libsmb.h"
+#include "libsmb/namequery.h"
#include "auth_info.h"
#include "../libcli/auth/libcli_auth.h"
#include "../libcli/auth/spnego.h"
#include "libsmb/nmblib.h"
#include "librpc/ndr/libndr.h"
#include "../libcli/smb/smbXcli_base.h"
+#include "../libcli/smb/smb_seal.h"
+#include "lib/param/param.h"
+#include "../libcli/smb/smb2_negotiate_context.h"
+#include "libads/krb5_errs.h"
#define STAR_SMBSERVER "*SMBSERVER"
+static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx,
+ const char *principal);
+
+struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx,
+ const char *username,
+ const char *domain,
+ const char *realm,
+ const char *password,
+ bool use_kerberos,
+ bool fallback_after_kerberos,
+ bool use_ccache,
+ bool password_is_nt_hash)
+{
+ struct loadparm_context *lp_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+ const char *principal = NULL;
+ char *tmp = NULL;
+ char *p = NULL;
+ bool ok;
+
+ creds = cli_credentials_init(mem_ctx);
+ if (creds == NULL) {
+ return NULL;
+ }
+
+ lp_ctx = loadparm_init_s3(creds, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ goto fail;
+ }
+ cli_credentials_set_conf(creds, lp_ctx);
+
+ if (username == NULL) {
+ username = "";
+ }
+
+ if (strlen(username) == 0) {
+ if (password != NULL && strlen(password) == 0) {
+ /*
+ * some callers pass "" as no password
+ *
+ * gensec only handles NULL as no password.
+ */
+ password = NULL;
+ }
+ if (password == NULL) {
+ cli_credentials_set_anonymous(creds);
+ return creds;
+ }
+ }
+
+ tmp = talloc_strdup(creds, username);
+ if (tmp == NULL) {
+ goto fail;
+ }
+ username = tmp;
+
+ /* allow for workgroups as part of the username */
+ if ((p = strchr_m(tmp, '\\')) ||
+ (p = strchr_m(tmp, '/')) ||
+ (p = strchr_m(tmp, *lp_winbind_separator()))) {
+ *p = 0;
+ username = p + 1;
+ domain = tmp;
+ }
+
+ principal = username;
+ username = cli_session_setup_get_account(creds, principal);
+ if (username == NULL) {
+ goto fail;
+ }
+ ok = strequal(username, principal);
+ if (ok) {
+ /*
+ * Ok still the same, so it's not a principal
+ */
+ principal = NULL;
+ }
+
+ if (use_kerberos && fallback_after_kerberos) {
+ cli_credentials_set_kerberos_state(creds,
+ CRED_AUTO_USE_KERBEROS);
+ } else if (use_kerberos) {
+ cli_credentials_set_kerberos_state(creds,
+ CRED_MUST_USE_KERBEROS);
+ } else {
+ cli_credentials_set_kerberos_state(creds,
+ CRED_DONT_USE_KERBEROS);
+ }
+
+ if (use_ccache) {
+ uint32_t features;
+
+ features = cli_credentials_get_gensec_features(creds);
+ features |= GENSEC_FEATURE_NTLM_CCACHE;
+ cli_credentials_set_gensec_features(creds, features);
+
+ if (password != NULL && strlen(password) == 0) {
+ /*
+ * some callers pass "" as no password
+ *
+ * GENSEC_FEATURE_NTLM_CCACHE only handles
+ * NULL as no password.
+ */
+ password = NULL;
+ }
+ }
+
+ ok = cli_credentials_set_username(creds,
+ username,
+ CRED_SPECIFIED);
+ if (!ok) {
+ goto fail;
+ }
+
+ if (domain != NULL) {
+ ok = cli_credentials_set_domain(creds,
+ domain,
+ CRED_SPECIFIED);
+ if (!ok) {
+ goto fail;
+ }
+ }
+
+ if (principal != NULL) {
+ ok = cli_credentials_set_principal(creds,
+ principal,
+ CRED_SPECIFIED);
+ if (!ok) {
+ goto fail;
+ }
+ }
+
+ if (realm != NULL) {
+ ok = cli_credentials_set_realm(creds,
+ realm,
+ CRED_SPECIFIED);
+ if (!ok) {
+ goto fail;
+ }
+ }
+
+ if (password != NULL && strlen(password) > 0) {
+ if (password_is_nt_hash) {
+ struct samr_Password nt_hash;
+ size_t converted;
+
+ converted = strhex_to_str((char *)nt_hash.hash,
+ sizeof(nt_hash.hash),
+ password,
+ strlen(password));
+ if (converted != sizeof(nt_hash.hash)) {
+ goto fail;
+ }
+
+ ok = cli_credentials_set_nt_hash(creds,
+ &nt_hash,
+ CRED_SPECIFIED);
+ if (!ok) {
+ goto fail;
+ }
+ } else {
+ ok = cli_credentials_set_password(creds,
+ password,
+ CRED_SPECIFIED);
+ if (!ok) {
+ goto fail;
+ }
+ }
+ }
+
+ return creds;
+fail:
+ TALLOC_FREE(creds);
+ return NULL;
+}
+
+NTSTATUS cli_session_creds_prepare_krb5(struct cli_state *cli,
+ struct cli_credentials *creds)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char *user_principal = NULL;
+ const char *user_account = NULL;
+ const char *user_domain = NULL;
+ const char *pass = NULL;
+ const char *target_hostname = NULL;
+ const DATA_BLOB *server_blob = NULL;
+ bool got_kerberos_mechanism = false;
+ enum credentials_use_kerberos krb5_state;
+ bool try_kerberos = false;
+ bool need_kinit = false;
+ bool auth_requested = true;
+ int ret;
+
+ target_hostname = smbXcli_conn_remote_name(cli->conn);
+ server_blob = smbXcli_conn_server_gss_blob(cli->conn);
+
+ /* the server might not even do spnego */
+ if (server_blob != NULL && server_blob->length != 0) {
+ char *OIDs[ASN1_MAX_OIDS] = { NULL, };
+ size_t i;
+ bool ok;
+
+ /*
+ * The server sent us the first part of the SPNEGO exchange in the
+ * negprot reply. It is WRONG to depend on the principal sent in the
+ * negprot reply, but right now we do it. If we don't receive one,
+ * we try to best guess, then fall back to NTLM.
+ */
+ ok = spnego_parse_negTokenInit(frame,
+ *server_blob,
+ OIDs,
+ NULL,
+ NULL);
+ if (!ok) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (OIDs[0] == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* make sure the server understands kerberos */
+ for (i = 0; OIDs[i] != NULL; 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) {
+ got_kerberos_mechanism = true;
+ break;
+ }
+ }
+ }
+
+ auth_requested = cli_credentials_authentication_requested(creds);
+ if (auth_requested) {
+ errno = 0;
+ user_principal = cli_credentials_get_principal(creds, frame);
+ if (errno != 0) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ user_account = cli_credentials_get_username(creds);
+ user_domain = cli_credentials_get_domain(creds);
+ pass = cli_credentials_get_password(creds);
+
+ krb5_state = cli_credentials_get_kerberos_state(creds);
+
+ if (krb5_state != CRED_DONT_USE_KERBEROS) {
+ try_kerberos = true;
+ }
+
+ if (user_principal == NULL) {
+ try_kerberos = false;
+ }
+
+ if (target_hostname == NULL) {
+ try_kerberos = false;
+ } else if (is_ipaddress(target_hostname)) {
+ try_kerberos = false;
+ } else if (strequal(target_hostname, "localhost")) {
+ try_kerberos = false;
+ } else if (strequal(target_hostname, STAR_SMBSERVER)) {
+ try_kerberos = false;
+ } else if (!auth_requested) {
+ try_kerberos = false;
+ }
+
+ if (krb5_state == CRED_MUST_USE_KERBEROS && !try_kerberos) {
+ DEBUG(0, ("Kerberos auth with '%s' (%s\\%s) to access "
+ "'%s' not possible\n",
+ user_principal, user_domain, user_account,
+ target_hostname));
+ TALLOC_FREE(frame);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (pass == NULL || strlen(pass) == 0) {
+ need_kinit = false;
+ } else if (krb5_state == CRED_MUST_USE_KERBEROS) {
+ need_kinit = try_kerberos;
+ } else if (!got_kerberos_mechanism) {
+ /*
+ * Most likely the server doesn't support
+ * Kerberos, don't waste time doing a kinit
+ */
+ need_kinit = false;
+ } else {
+ need_kinit = try_kerberos;
+ }
+
+ if (!need_kinit) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+
+ /*
+ * TODO: This should be done within the gensec layer
+ * only if required!
+ */
+ setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1);
+ ret = kerberos_kinit_password(user_principal, pass,
+ 0 /* no time correction for now */,
+ NULL);
+ if (ret != 0) {
+ int dbglvl = DBGLVL_NOTICE;
+
+ if (krb5_state == CRED_MUST_USE_KERBEROS) {
+ dbglvl = DBGLVL_ERR;
+ }
+
+ DEBUG(dbglvl, ("Kinit for %s to access %s failed: %s\n",
+ user_principal, target_hostname,
+ error_message(ret)));
+ if (krb5_state == CRED_MUST_USE_KERBEROS) {
+ TALLOC_FREE(frame);
+ return krb5_to_nt_status(ret);
+ }
+
+ /*
+ * Ignore the error and hope that NTLM will work
+ */
+ }
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cli_state_update_after_sesssetup(struct cli_state *cli,
+ const char *native_os,
+ const char *native_lm,
+ const char *primary_domain)
+{
+#define _VALID_STR(p) ((p) != NULL && (p)[0] != '\0')
+
+ if (!_VALID_STR(cli->server_os) && _VALID_STR(native_os)) {
+ cli->server_os = talloc_strdup(cli, native_os);
+ if (cli->server_os == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (!_VALID_STR(cli->server_type) && _VALID_STR(native_lm)) {
+ cli->server_type = talloc_strdup(cli, native_lm);
+ if (cli->server_type == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (!_VALID_STR(cli->server_domain) && _VALID_STR(primary_domain)) {
+ cli->server_domain = talloc_strdup(cli, primary_domain);
+ if (cli->server_domain == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+#undef _VALID_STRING
+ return NT_STATUS_OK;
+}
+
/********************************************************
Utility function to ensure we always return at least
a valid char * pointer to an empty string for the
}
/****************************************************************************
- Do an old lanman2 style session setup.
+ Work out suitable capabilities to offer the server.
+****************************************************************************/
+
+static uint32_t cli_session_setup_capabilities(struct cli_state *cli,
+ uint32_t sesssetup_capabilities)
+{
+ uint32_t client_capabilities = smb1cli_conn_capabilities(cli->conn);
+
+ /*
+ * We only send capabilities based on the mask for:
+ * - client only flags
+ * - flags used in both directions
+ *
+ * We do not echo the server only flags, except some legacy flags.
+ *
+ * SMB_CAP_LEGACY_CLIENT_MASK contains CAP_LARGE_READX and
+ * CAP_LARGE_WRITEX in order to allow us to do large reads
+ * against old Samba releases (<= 3.6.x).
+ */
+ client_capabilities &= (SMB_CAP_BOTH_MASK | SMB_CAP_LEGACY_CLIENT_MASK);
+
+ /*
+ * Session Setup specific flags CAP_DYNAMIC_REAUTH
+ * and CAP_EXTENDED_SECURITY are passed by the caller.
+ * We need that in order to do guest logins even if
+ * CAP_EXTENDED_SECURITY is negotiated.
+ */
+ client_capabilities &= ~(CAP_DYNAMIC_REAUTH|CAP_EXTENDED_SECURITY);
+ sesssetup_capabilities &= (CAP_DYNAMIC_REAUTH|CAP_EXTENDED_SECURITY);
+ client_capabilities |= sesssetup_capabilities;
+
+ return client_capabilities;
+}
+
+/****************************************************************************
+ Do a NT1 guest session setup.
****************************************************************************/
-struct cli_session_setup_lanman2_state {
+struct cli_session_setup_guest_state {
struct cli_state *cli;
- uint16_t vwv[10];
- const char *user;
+ uint16_t vwv[13];
+ struct iovec bytes;
};
-static void cli_session_setup_lanman2_done(struct tevent_req *subreq);
+static void cli_session_setup_guest_done(struct tevent_req *subreq);
-static struct tevent_req *cli_session_setup_lanman2_send(
- TALLOC_CTX *mem_ctx, struct tevent_context *ev,
- struct cli_state *cli, const char *user,
- const char *pass, size_t passlen,
- const char *workgroup)
+struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ struct tevent_req **psmbreq)
{
struct tevent_req *req, *subreq;
- struct cli_session_setup_lanman2_state *state;
- DATA_BLOB lm_response = data_blob_null;
+ struct cli_session_setup_guest_state *state;
uint16_t *vwv;
uint8_t *bytes;
- char *tmp;
- uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
req = tevent_req_create(mem_ctx, &state,
- struct cli_session_setup_lanman2_state);
+ struct cli_session_setup_guest_state);
if (req == NULL) {
return NULL;
}
state->cli = cli;
- state->user = user;
vwv = state->vwv;
- /*
- * if in share level security then don't send a password now
- */
- if (!(sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
- passlen = 0;
- }
-
- if (passlen > 0
- && (sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE)
- && passlen != 24) {
- /*
- * Encrypted mode needed, and non encrypted password
- * supplied.
- */
- lm_response = data_blob(NULL, 24);
- if (tevent_req_nomem(lm_response.data, req)) {
- return tevent_req_post(req, ev);
- }
-
- if (!SMBencrypt(pass, smb1cli_conn_server_challenge(cli->conn),
- (uint8_t *)lm_response.data)) {
- DEBUG(1, ("Password is > 14 chars in length, and is "
- "therefore incompatible with Lanman "
- "authentication\n"));
- tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
- return tevent_req_post(req, ev);
- }
- } else if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE)
- && passlen == 24) {
- /*
- * Encrypted mode needed, and encrypted password
- * supplied.
- */
- lm_response = data_blob(pass, passlen);
- if (tevent_req_nomem(lm_response.data, req)) {
- return tevent_req_post(req, ev);
- }
- } else if (passlen > 0) {
- uint8_t *buf;
- size_t converted_size;
- /*
- * Plaintext mode needed, assume plaintext supplied.
- */
- buf = talloc_array(talloc_tos(), uint8_t, 0);
- buf = smb_bytes_push_str(buf, smbXcli_conn_use_unicode(cli->conn), pass, passlen+1,
- &converted_size);
- if (tevent_req_nomem(buf, req)) {
- return tevent_req_post(req, ev);
- }
- lm_response = data_blob(pass, passlen);
- TALLOC_FREE(buf);
- if (tevent_req_nomem(lm_response.data, req)) {
- return tevent_req_post(req, ev);
- }
- }
-
- SCVAL(vwv+0, 0, 0xff);
+ 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, 1);
+ SSVAL(vwv+4, 0, cli_state_get_vc_num(cli));
SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
- SSVAL(vwv+7, 0, lm_response.length);
+ 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, 0));
- bytes = talloc_array(state, uint8_t, lm_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);
- }
- data_blob_free(&lm_response);
+ bytes = talloc_array(state, uint8_t, 0);
- tmp = talloc_strdup_upper(talloc_tos(), user);
- if (tevent_req_nomem(tmp, req)) {
- return tevent_req_post(req, ev);
- }
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), tmp, strlen(tmp)+1,
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, /* username */
NULL);
- TALLOC_FREE(tmp);
-
- tmp = talloc_strdup_upper(talloc_tos(), workgroup);
- if (tevent_req_nomem(tmp, req)) {
- return tevent_req_post(req, ev);
- }
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), tmp, strlen(tmp)+1,
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, /* workgroup */
NULL);
bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL);
bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL);
- if (tevent_req_nomem(bytes, req)) {
- return tevent_req_post(req, ev);
- }
-
- subreq = cli_smb_send(state, ev, cli, SMBsesssetupX, 0, 10, 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_lanman2_done, req);
- return req;
-}
-
-static void cli_session_setup_lanman2_done(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct cli_session_setup_lanman2_state *state = tevent_req_data(
- req, struct cli_session_setup_lanman2_state);
- struct cli_state *cli = state->cli;
- uint32_t num_bytes;
- uint8_t *in;
- uint8_t *inhdr;
- uint8_t *bytes;
- uint8_t *p;
- NTSTATUS status;
- ssize_t ret;
- uint8_t wct;
- uint16_t *vwv;
-
- status = cli_smb_recv(subreq, state, &in, 3, &wct, &vwv,
- &num_bytes, &bytes);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
-
- inhdr = in + NBT_HDR_SIZE;
- p = bytes;
-
- cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
-
- status = smb_bytes_talloc_string(cli,
- inhdr,
- &cli->server_os,
- p,
- bytes+num_bytes-p,
- &ret);
-
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
- p += ret;
-
- status = smb_bytes_talloc_string(cli,
- inhdr,
- &cli->server_type,
- p,
- bytes+num_bytes-p,
- &ret);
-
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
- p += ret;
-
- status = smb_bytes_talloc_string(cli,
- inhdr,
- &cli->server_domain,
- p,
- bytes+num_bytes-p,
- &ret);
-
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
- p += ret;
-
- tevent_req_done(req);
-}
-
-static NTSTATUS cli_session_setup_lanman2_recv(struct tevent_req *req)
-{
- return tevent_req_simple_recv_ntstatus(req);
-}
-
-/****************************************************************************
- Work out suitable capabilities to offer the server.
-****************************************************************************/
-
-static uint32_t cli_session_setup_capabilities(struct cli_state *cli,
- uint32_t sesssetup_capabilities)
-{
- uint32_t client_capabilities = smb1cli_conn_capabilities(cli->conn);
-
- /*
- * We only send capabilities based on the mask for:
- * - client only flags
- * - flags used in both directions
- *
- * We do not echo the server only flags, except some legacy flags.
- *
- * SMB_CAP_LEGACY_CLIENT_MASK contains CAP_LARGE_READX and
- * CAP_LARGE_WRITEX in order to allow us to do large reads
- * against old Samba releases (<= 3.6.x).
- */
- client_capabilities &= (SMB_CAP_BOTH_MASK | SMB_CAP_LEGACY_CLIENT_MASK);
-
- /*
- * Session Setup specific flags CAP_DYNAMIC_REAUTH
- * and CAP_EXTENDED_SECURITY are passed by the caller.
- * We need that in order to do guest logins even if
- * CAP_EXTENDED_SECURITY is negotiated.
- */
- client_capabilities &= ~(CAP_DYNAMIC_REAUTH|CAP_EXTENDED_SECURITY);
- sesssetup_capabilities &= (CAP_DYNAMIC_REAUTH|CAP_EXTENDED_SECURITY);
- client_capabilities |= sesssetup_capabilities;
-
- return client_capabilities;
-}
-
-/****************************************************************************
- Do a NT1 guest session setup.
-****************************************************************************/
-
-struct cli_session_setup_guest_state {
- struct cli_state *cli;
- uint16_t vwv[13];
- struct iovec bytes;
-};
-
-static void cli_session_setup_guest_done(struct tevent_req *subreq);
-
-struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct cli_state *cli,
- struct tevent_req **psmbreq)
-{
- struct tevent_req *req, *subreq;
- struct cli_session_setup_guest_state *state;
- uint16_t *vwv;
- uint8_t *bytes;
-
- req = tevent_req_create(mem_ctx, &state,
- struct cli_session_setup_guest_state);
- if (req == NULL) {
- return NULL;
- }
- state->cli = cli;
- vwv = state->vwv;
-
- 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_state_get_vc_num(cli));
- SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
- 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, 0));
-
- bytes = talloc_array(state, uint8_t, 0);
-
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, /* username */
- NULL);
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, /* workgroup */
- NULL);
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL);
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL);
-
- if (bytes == NULL) {
- TALLOC_FREE(req);
- return NULL;
+ if (bytes == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
}
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);
+ subreq = cli_smb_req_create(state, ev, cli, SMBsesssetupX, 0, 0, 13,
+ vwv, 1, &state->bytes);
if (subreq == NULL) {
TALLOC_FREE(req);
return NULL;
p = bytes;
cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
+ smb1cli_session_set_action(cli->smb1.session, SVAL(vwv+2, 0));
status = smb_bytes_talloc_string(cli,
inhdr,
return tevent_req_simple_recv_ntstatus(req);
}
-/****************************************************************************
- Do a NT1 plaintext session setup.
-****************************************************************************/
+/* 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.
+ */
-struct cli_session_setup_plain_state {
+#define BASE_SESSSETUP_BLOB_PACKET_SIZE (35 + 24 + 22)
+
+struct cli_sesssetup_blob_state {
+ struct tevent_context *ev;
struct cli_state *cli;
- uint16_t vwv[13];
- const char *user;
+ DATA_BLOB blob;
+ uint16_t max_blob_size;
+
+ DATA_BLOB this_blob;
+ struct iovec *recv_iov;
+
+ NTSTATUS status;
+ const uint8_t *inbuf;
+ DATA_BLOB ret_blob;
+
+ char *out_native_os;
+ char *out_native_lm;
};
-static void cli_session_setup_plain_done(struct tevent_req *subreq);
+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_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)
+static struct tevent_req *cli_sesssetup_blob_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ DATA_BLOB blob)
{
struct tevent_req *req, *subreq;
- struct cli_session_setup_plain_state *state;
- uint16_t *vwv;
- uint8_t *bytes;
- size_t passlen;
- char *version;
+ struct cli_sesssetup_blob_state *state;
+ uint32_t usable_space;
req = tevent_req_create(mem_ctx, &state,
- struct cli_session_setup_plain_state);
+ struct cli_sesssetup_blob_state);
if (req == NULL) {
return NULL;
}
+ state->ev = ev;
+ state->blob = blob;
state->cli = cli;
- state->user = user;
- vwv = state->vwv;
-
- 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_state_get_vc_num(cli));
- SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
- 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, 0));
-
- bytes = talloc_array(state, uint8_t, 0);
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), pass, strlen(pass)+1,
- &passlen);
- if (tevent_req_nomem(bytes, req)) {
- return tevent_req_post(req, ev);
- }
- SSVAL(vwv + (smbXcli_conn_use_unicode(cli->conn) ? 8 : 7), 0, passlen);
-
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
- user, strlen(user)+1, NULL);
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
- workgroup, strlen(workgroup)+1, NULL);
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
- "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);
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ usable_space = UINT16_MAX;
+ } else {
+ usable_space = cli_state_available_size(cli,
+ BASE_SESSSETUP_BLOB_PACKET_SIZE);
}
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
- version, strlen(version)+1, NULL);
- TALLOC_FREE(version);
- if (tevent_req_nomem(bytes, req)) {
+ if (usable_space == 0) {
+ DEBUG(1, ("cli_session_setup_blob: cli->max_xmit too small "
+ "(not possible to send %u bytes)\n",
+ BASE_SESSSETUP_BLOB_PACKET_SIZE + 1));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
return tevent_req_post(req, ev);
}
+ state->max_blob_size = MIN(usable_space, 0xFFFF);
- subreq = cli_smb_send(state, ev, cli, SMBsesssetupX, 0, 13, vwv,
- talloc_get_size(bytes), bytes);
- if (tevent_req_nomem(subreq, req)) {
+ if (!cli_sesssetup_blob_next(state, &subreq)) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
return tevent_req_post(req, ev);
}
- tevent_req_set_callback(subreq, cli_session_setup_plain_done, req);
+ tevent_req_set_callback(subreq, cli_sesssetup_blob_done, req);
return req;
}
-static void cli_session_setup_plain_done(struct tevent_req *subreq)
+static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state,
+ struct tevent_req **psubreq)
{
- 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;
- uint8_t *inhdr;
- uint8_t *bytes;
- uint8_t *p;
- NTSTATUS status;
- ssize_t ret;
- uint8_t wct;
- uint16_t *vwv;
+ struct tevent_req *subreq;
+ uint16_t thistime;
- status = cli_smb_recv(subreq, state, &in, 3, &wct, &vwv,
- &num_bytes, &bytes);
- TALLOC_FREE(subreq);
- if (tevent_req_nterror(req, status)) {
- return;
- }
+ thistime = MIN(state->blob.length, state->max_blob_size);
- inhdr = in + NBT_HDR_SIZE;
- p = bytes;
+ state->this_blob.data = state->blob.data;
+ state->this_blob.length = thistime;
- cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
+ state->blob.data += thistime;
+ state->blob.length -= thistime;
- status = smb_bytes_talloc_string(cli,
- inhdr,
- &cli->server_os,
- p,
- bytes+num_bytes-p,
- &ret);
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = smb2cli_session_setup_send(state, state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ 0, /* in_flags */
+ SMB2_CAP_DFS, /* in_capabilities */
+ 0, /* in_channel */
+ 0, /* in_previous_session_id */
+ &state->this_blob);
+ if (subreq == NULL) {
+ return false;
+ }
+ } else {
+ uint16_t in_buf_size = 0;
+ uint16_t in_mpx_max = 0;
+ uint16_t in_vc_num = 0;
+ uint32_t in_sess_key = 0;
+ uint32_t in_capabilities = 0;
+ const char *in_native_os = NULL;
+ const char *in_native_lm = NULL;
+
+ in_buf_size = CLI_BUFFER_SIZE;
+ in_mpx_max = smbXcli_conn_max_requests(state->cli->conn);
+ in_vc_num = cli_state_get_vc_num(state->cli);
+ in_sess_key = smb1cli_conn_server_session_key(state->cli->conn);
+ in_capabilities = cli_session_setup_capabilities(state->cli,
+ CAP_EXTENDED_SECURITY);
+ in_native_os = "Unix";
+ in_native_lm = "Samba";
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
+ /*
+ * For now we keep the same values as before,
+ * we may remove these in a separate commit later.
+ */
+ in_mpx_max = 2;
+ in_vc_num = 1;
+ in_sess_key = 0;
+
+ subreq = smb1cli_session_setup_ext_send(state, state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb1.pid,
+ state->cli->smb1.session,
+ in_buf_size,
+ in_mpx_max,
+ in_vc_num,
+ in_sess_key,
+ state->this_blob,
+ in_capabilities,
+ in_native_os,
+ in_native_lm);
+ if (subreq == NULL) {
+ return false;
+ }
}
- p += ret;
+ *psubreq = subreq;
+ return true;
+}
- status = smb_bytes_talloc_string(cli,
- inhdr,
- &cli->server_type,
- p,
- bytes+num_bytes-p,
- &ret);
+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);
+ NTSTATUS status;
- if (!NT_STATUS_IS_OK(status)) {
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ status = smb2cli_session_setup_recv(subreq, state,
+ &state->recv_iov,
+ &state->ret_blob);
+ } else {
+ status = smb1cli_session_setup_ext_recv(subreq, state,
+ &state->recv_iov,
+ &state->inbuf,
+ &state->ret_blob,
+ &state->out_native_os,
+ &state->out_native_lm);
+ }
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)
+ && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
tevent_req_nterror(req, status);
return;
}
- p += ret;
- status = smb_bytes_talloc_string(cli,
- inhdr,
- &cli->server_domain,
- p,
- bytes+num_bytes-p,
- &ret);
+ state->status = status;
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
+ status = cli_state_update_after_sesssetup(state->cli,
+ state->out_native_os,
+ state->out_native_lm,
+ NULL);
+ if (tevent_req_nterror(req, status)) {
return;
}
- p += ret;
+ if (state->blob.length != 0) {
+ /*
+ * More to send
+ */
+ if (!cli_sesssetup_blob_next(state, &subreq)) {
+ tevent_req_oom(req);
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_sesssetup_blob_done, req);
+ return;
+ }
tevent_req_done(req);
}
-static NTSTATUS cli_session_setup_plain_recv(struct tevent_req *req)
+static NTSTATUS cli_sesssetup_blob_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *pblob,
+ const uint8_t **pinbuf,
+ struct iovec **precv_iov)
{
- return tevent_req_simple_recv_ntstatus(req);
+ struct cli_sesssetup_blob_state *state = tevent_req_data(
+ req, struct cli_sesssetup_blob_state);
+ NTSTATUS status;
+ struct iovec *recv_iov;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ TALLOC_FREE(state->cli->smb2.session);
+ cli_state_set_uid(state->cli, UID_FIELD_INVALID);
+ tevent_req_received(req);
+ return status;
+ }
+
+ recv_iov = talloc_move(mem_ctx, &state->recv_iov);
+ if (pblob != NULL) {
+ *pblob = state->ret_blob;
+ }
+ if (pinbuf != NULL) {
+ *pinbuf = state->inbuf;
+ }
+ if (precv_iov != NULL) {
+ *precv_iov = recv_iov;
+ }
+ /* could be NT_STATUS_MORE_PROCESSING_REQUIRED */
+ status = state->status;
+ tevent_req_received(req);
+ return status;
}
/****************************************************************************
- do a NT1 NTLM/LM encrypted session setup - for when extended security
- is not negotiated.
- @param cli client state to create do session setup on
- @param user username
- @param pass *either* cleartext password (passlen !=24) or LM response.
- @param ntpass NT response, implies ntpasslen >=24, implies pass is not clear
- @param workgroup The user's domain.
+ Do a spnego/NTLMSSP encrypted session setup.
****************************************************************************/
-struct cli_session_setup_nt1_state {
+struct cli_session_setup_gensec_state {
+ struct tevent_context *ev;
struct cli_state *cli;
- uint16_t vwv[13];
- DATA_BLOB response;
+ struct auth_generic_state *auth_generic;
+ bool is_anonymous;
+ DATA_BLOB blob_in;
+ const uint8_t *inbuf;
+ struct iovec *recv_iov;
+ DATA_BLOB blob_out;
+ bool local_ready;
+ bool remote_ready;
DATA_BLOB session_key;
- const char *user;
};
-static void cli_session_setup_nt1_done(struct tevent_req *subreq);
+static int cli_session_setup_gensec_state_destructor(
+ struct cli_session_setup_gensec_state *state)
+{
+ TALLOC_FREE(state->auth_generic);
+ data_blob_clear_free(&state->session_key);
+ return 0;
+}
-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)
+static void cli_session_setup_gensec_local_next(struct tevent_req *req);
+static void cli_session_setup_gensec_local_done(struct tevent_req *subreq);
+static void cli_session_setup_gensec_remote_next(struct tevent_req *req);
+static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq);
+static void cli_session_setup_gensec_ready(struct tevent_req *req);
+
+static struct tevent_req *cli_session_setup_gensec_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ struct cli_credentials *creds,
+ const char *target_service,
+ const char *target_hostname)
{
- 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;
- uint16_t *vwv;
- uint8_t *bytes;
- char *workgroup_upper;
+ struct tevent_req *req;
+ struct cli_session_setup_gensec_state *state;
+ NTSTATUS status;
+ const DATA_BLOB *b = NULL;
req = tevent_req_create(mem_ctx, &state,
- struct cli_session_setup_nt1_state);
+ struct cli_session_setup_gensec_state);
if (req == NULL) {
return NULL;
}
+ state->ev = ev;
state->cli = cli;
- state->user = user;
- vwv = state->vwv;
- if (passlen == 0) {
- /* do nothing - guest login */
- } else if (passlen != 24) {
- if (lp_client_ntlmv2_auth()) {
- DATA_BLOB server_chal;
- DATA_BLOB names_blob;
-
- server_chal =
- data_blob_const(smb1cli_conn_server_challenge(cli->conn),
- 8);
-
- /*
- * note that the 'workgroup' here is a best
- * guess - we don't know the server's domain
- * at this point. Windows clients also don't
- * use hostname...
- */
- names_blob = NTLMv2_generate_names_blob(
- NULL, NULL, 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)) {
- data_blob_free(&names_blob);
- tevent_req_nterror(
- req, NT_STATUS_ACCESS_DENIED);
- return tevent_req_post(req, ev);
- }
- data_blob_free(&names_blob);
-
- } else {
- uchar nt_hash[16];
- E_md4hash(pass, nt_hash);
-
-#ifdef LANMAN_ONLY
- nt_response = data_blob_null;
-#else
- nt_response = data_blob(NULL, 24);
- if (tevent_req_nomem(nt_response.data, req)) {
- return tevent_req_post(req, ev);
- }
-
- SMBNTencrypt(pass, smb1cli_conn_server_challenge(cli->conn),
- nt_response.data);
-#endif
- /* non encrypted password supplied. Ignore ntpass. */
- if (lp_client_lanman_auth()) {
-
- lm_response = data_blob(NULL, 24);
- if (tevent_req_nomem(lm_response.data, req)) {
- return tevent_req_post(req, ev);
- }
-
- if (!SMBencrypt(pass,
- smb1cli_conn_server_challenge(cli->conn),
- 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);
- }
- } else {
- /*
- * LM disabled, place NT# in LM field
- * instead
- */
- lm_response = data_blob(
- nt_response.data, nt_response.length);
- }
+ talloc_set_destructor(
+ state, cli_session_setup_gensec_state_destructor);
- if (tevent_req_nomem(lm_response.data, req)) {
- return tevent_req_post(req, ev);
- }
+ status = auth_generic_client_prepare(state, &state->auth_generic);
+ if (tevent_req_nterror(req, status)) {
+ 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);
-#else
- SMBsesskeygen_ntv1(nt_hash, session_key.data);
-#endif
- }
- } else {
- /* pre-encrypted password supplied. Only used for
- security=server, can't do
- signing because we don't have original key */
+ status = auth_generic_set_creds(state->auth_generic, creds);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
- lm_response = data_blob(pass, passlen);
- if (tevent_req_nomem(lm_response.data, req)) {
- return tevent_req_post(req, ev);
- }
+ gensec_want_feature(state->auth_generic->gensec_security,
+ GENSEC_FEATURE_SESSION_KEY);
- nt_response = data_blob(ntpass, ntpasslen);
- if (tevent_req_nomem(nt_response.data, req)) {
+ if (target_service != NULL) {
+ status = gensec_set_target_service(
+ state->auth_generic->gensec_security,
+ target_service);
+ if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
}
-#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);
- }
-
- 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)) {
+ if (target_hostname != NULL) {
+ status = gensec_set_target_hostname(
+ state->auth_generic->gensec_security,
+ target_hostname);
+ if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
}
- data_blob_free(&session_key);
-
- 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_state_get_vc_num(cli));
- SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
- 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, 0));
- 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);
+ b = smbXcli_conn_server_gss_blob(cli->conn);
+ if (b != NULL) {
+ state->blob_in = *b;
}
- if (nt_response.length != 0) {
- memcpy(bytes + lm_response.length,
- nt_response.data, nt_response.length);
- }
- data_blob_free(&lm_response);
- data_blob_free(&nt_response);
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
- user, strlen(user)+1, NULL);
+ state->is_anonymous = cli_credentials_is_anonymous(state->auth_generic->credentials);
- /*
- * Upper case here might help some NTLMv2 implementations
- */
- workgroup_upper = talloc_strdup_upper(talloc_tos(), workgroup);
- if (tevent_req_nomem(workgroup_upper, req)) {
+ status = auth_generic_client_start(state->auth_generic,
+ GENSEC_OID_SPNEGO);
+ if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
- workgroup_upper, strlen(workgroup_upper)+1,
- NULL);
- TALLOC_FREE(workgroup_upper);
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL);
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL);
- if (tevent_req_nomem(bytes, req)) {
- return tevent_req_post(req, ev);
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ state->cli->smb2.session = smbXcli_session_create(cli,
+ cli->conn);
+ if (tevent_req_nomem(state->cli->smb2.session, req)) {
+ return tevent_req_post(req, ev);
+ }
}
- subreq = cli_smb_send(state, ev, cli, SMBsesssetupX, 0, 13, vwv,
- talloc_get_size(bytes), bytes);
- if (tevent_req_nomem(subreq, req)) {
+ cli_session_setup_gensec_local_next(req);
+ if (!tevent_req_is_in_progress(req)) {
return tevent_req_post(req, ev);
}
- tevent_req_set_callback(subreq, cli_session_setup_nt1_done, req);
+
return req;
}
-static void cli_session_setup_nt1_done(struct tevent_req *subreq)
+static void cli_session_setup_gensec_local_next(struct tevent_req *req)
{
- 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;
- uint8_t *inhdr;
- uint8_t *bytes;
- uint8_t *p;
- NTSTATUS status;
- ssize_t ret;
- uint8_t wct;
- uint16_t *vwv;
+ struct cli_session_setup_gensec_state *state =
+ tevent_req_data(req,
+ struct cli_session_setup_gensec_state);
+ struct tevent_req *subreq = NULL;
- status = cli_smb_recv(subreq, state, &in, 3, &wct, &vwv,
- &num_bytes, &bytes);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
+ if (state->local_ready) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
return;
}
- inhdr = in + NBT_HDR_SIZE;
- p = bytes;
-
- cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
-
- status = smb_bytes_talloc_string(cli,
- inhdr,
- &cli->server_os,
- p,
- bytes+num_bytes-p,
- &ret);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
+ subreq = gensec_update_send(state, state->ev,
+ state->auth_generic->gensec_security,
+ state->blob_in);
+ if (tevent_req_nomem(subreq, req)) {
return;
}
- p += ret;
+ tevent_req_set_callback(subreq, cli_session_setup_gensec_local_done, req);
+}
- status = smb_bytes_talloc_string(cli,
- inhdr,
- &cli->server_type,
- p,
- bytes+num_bytes-p,
- &ret);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
- p += ret;
+static void cli_session_setup_gensec_local_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cli_session_setup_gensec_state *state =
+ tevent_req_data(req,
+ struct cli_session_setup_gensec_state);
+ NTSTATUS status;
- status = smb_bytes_talloc_string(cli,
- inhdr,
- &cli->server_domain,
- p,
- bytes+num_bytes-p,
- &ret);
- if (!NT_STATUS_IS_OK(status)) {
+ status = gensec_update_recv(subreq, state, &state->blob_out);
+ TALLOC_FREE(subreq);
+ state->blob_in = data_blob_null;
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+ {
tevent_req_nterror(req, status);
return;
}
- p += ret;
- if (smb1cli_conn_activate_signing(cli->conn, state->session_key, state->response)
- && !smb1cli_conn_check_signing(cli->conn, (uint8_t *)in, 1)) {
- tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
- return;
+ if (NT_STATUS_IS_OK(status)) {
+ state->local_ready = true;
}
- if (state->session_key.data) {
- struct smbXcli_session *session = state->cli->smb1.session;
- status = smb1cli_session_set_session_key(session,
- state->session_key);
- if (tevent_req_nterror(req, status)) {
- return;
- }
+ if (state->local_ready && state->remote_ready) {
+ cli_session_setup_gensec_ready(req);
+ return;
}
- tevent_req_done(req);
-}
-static NTSTATUS cli_session_setup_nt1_recv(struct tevent_req *req)
-{
- return tevent_req_simple_recv_ntstatus(req);
+ cli_session_setup_gensec_remote_next(req);
}
-/* 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.
- */
-
-#define BASE_SESSSETUP_BLOB_PACKET_SIZE (35 + 24 + 22)
-
-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;
-
- DATA_BLOB smb2_blob;
- struct iovec *recv_iov;
-
- NTSTATUS status;
- uint8_t *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)
+static void cli_session_setup_gensec_remote_next(struct tevent_req *req)
{
- struct tevent_req *req, *subreq;
- struct cli_sesssetup_blob_state *state;
- uint32_t usable_space;
-
- 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 (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
- usable_space = UINT16_MAX;
- } else {
- usable_space = cli_state_available_size(cli,
- BASE_SESSSETUP_BLOB_PACKET_SIZE);
- }
+ struct cli_session_setup_gensec_state *state =
+ tevent_req_data(req,
+ struct cli_session_setup_gensec_state);
+ struct tevent_req *subreq = NULL;
- if (usable_space == 0) {
- DEBUG(1, ("cli_session_setup_blob: cli->max_xmit too small "
- "(not possible to send %u bytes)\n",
- BASE_SESSSETUP_BLOB_PACKET_SIZE + 1));
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return tevent_req_post(req, ev);
+ if (state->remote_ready) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
}
- state->max_blob_size = MIN(usable_space, 0xFFFF);
- if (!cli_sesssetup_blob_next(state, &subreq)) {
- tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
- return tevent_req_post(req, ev);
+ 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_sesssetup_blob_done, req);
- return req;
+ tevent_req_set_callback(subreq,
+ cli_session_setup_gensec_remote_done,
+ req);
}
-static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state,
- struct tevent_req **psubreq)
+static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq)
{
- struct tevent_req *subreq;
- uint16_t thistime;
-
- thistime = MIN(state->blob.length, state->max_blob_size);
-
- if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
-
- state->smb2_blob.data = state->blob.data;
- state->smb2_blob.length = thistime;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cli_session_setup_gensec_state *state =
+ tevent_req_data(req,
+ struct cli_session_setup_gensec_state);
+ NTSTATUS status;
- state->blob.data += thistime;
- state->blob.length -= thistime;
+ state->inbuf = NULL;
+ TALLOC_FREE(state->recv_iov);
- subreq = smb2cli_session_setup_send(state, state->ev,
- state->cli->conn,
- state->cli->timeout,
- state->cli->smb2.session,
- 0, /* in_flags */
- SMB2_CAP_DFS, /* in_capabilities */
- 0, /* in_channel */
- 0, /* in_previous_session_id */
- &state->smb2_blob);
- if (subreq == NULL) {
- return false;
- }
- *psubreq = subreq;
- return true;
+ status = cli_sesssetup_blob_recv(subreq, state, &state->blob_in,
+ &state->inbuf, &state->recv_iov);
+ TALLOC_FREE(subreq);
+ data_blob_free(&state->blob_out);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+ {
+ tevent_req_nterror(req, status);
+ return;
}
- 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);
+ if (NT_STATUS_IS_OK(status)) {
+ struct smbXcli_session *session = NULL;
+ bool is_guest = false;
- SSVAL(state->vwv+7, 0, thistime);
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ session = state->cli->smb2.session;
+ } else {
+ session = state->cli->smb1.session;
+ }
- 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));
+ is_guest = smbXcli_session_is_guest(session);
+ if (is_guest) {
+ /*
+ * We can't finish the gensec handshake, we don't
+ * have a negotiated session key.
+ *
+ * So just pretend we are completely done,
+ * we need to continue as anonymous from this point,
+ * as we can't get a session key.
+ *
+ * Note that smbXcli_session_is_guest()
+ * always returns false if we require signing.
+ */
+ state->blob_in = data_blob_null;
+ state->local_ready = true;
+ state->is_anonymous = true;
+ }
- state->buf = (uint8_t *)talloc_memdup(state, state->blob.data,
- thistime);
- if (state->buf == NULL) {
- return false;
+ state->remote_ready = true;
}
- state->blob.data += thistime;
- state->blob.length -= thistime;
- state->buf = smb_bytes_push_str(state->buf, smbXcli_conn_use_unicode(state->cli->conn),
- "Unix", 5, NULL);
- state->buf = smb_bytes_push_str(state->buf, smbXcli_conn_use_unicode(state->cli->conn),
- "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;
+ if (state->local_ready && state->remote_ready) {
+ cli_session_setup_gensec_ready(req);
+ return;
}
- *psubreq = subreq;
- return true;
+
+ cli_session_setup_gensec_local_next(req);
}
-static void cli_sesssetup_blob_done(struct tevent_req *subreq)
+static void cli_session_setup_gensec_ready(struct tevent_req *req)
{
- 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;
+ struct cli_session_setup_gensec_state *state =
+ tevent_req_data(req,
+ struct cli_session_setup_gensec_state);
+ const char *server_domain = NULL;
NTSTATUS status;
- uint8_t *p;
- uint16_t blob_length;
- uint8_t *in;
- uint8_t *inhdr;
- ssize_t ret;
- if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
- status = smb2cli_session_setup_recv(subreq, state,
- &state->recv_iov,
- &state->ret_blob);
- } else {
- status = cli_smb_recv(subreq, state, &in, 4, &wct, &vwv,
- &num_bytes, &bytes);
- TALLOC_FREE(state->buf);
- }
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)
- && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
- tevent_req_nterror(req, status);
+ if (state->blob_in.length != 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
return;
}
- state->status = status;
-
- if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
- goto next;
- }
-
- state->inbuf = in;
- inhdr = in + NBT_HDR_SIZE;
- cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
-
- blob_length = SVAL(vwv+3, 0);
- if (blob_length > num_bytes) {
+ if (state->blob_out.length != 0) {
tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
return;
}
- state->ret_blob = data_blob_const(bytes, blob_length);
- p = bytes + blob_length;
+ /*
+ * gensec_ntlmssp_server_domain() returns NULL
+ * if NTLMSSP is not used.
+ *
+ * We can remove this later
+ * and leave the server domain empty for SMB2 and above
+ * in future releases.
+ */
+ server_domain = gensec_ntlmssp_server_domain(
+ state->auth_generic->gensec_security);
- status = smb_bytes_talloc_string(cli,
- inhdr,
- &cli->server_os,
- p,
- bytes+num_bytes-p,
- &ret);
+ if (state->cli->server_domain[0] == '\0' && server_domain != NULL) {
+ TALLOC_FREE(state->cli->server_domain);
+ state->cli->server_domain = talloc_strdup(state->cli,
+ server_domain);
+ if (state->cli->server_domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ }
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
+ if (state->is_anonymous) {
+ /*
+ * Windows server does not set the
+ * SMB2_SESSION_FLAG_IS_NULL flag.
+ *
+ * This fix makes sure we do not try
+ * to verify a signature on the final
+ * session setup response.
+ */
+ tevent_req_done(req);
return;
}
- p += ret;
-
- status = smb_bytes_talloc_string(cli,
- inhdr,
- &cli->server_type,
- p,
- bytes+num_bytes-p,
- &ret);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
+ status = gensec_session_key(state->auth_generic->gensec_security,
+ state, &state->session_key);
+ if (tevent_req_nterror(req, status)) {
return;
}
- p += ret;
- status = smb_bytes_talloc_string(cli,
- inhdr,
- &cli->server_domain,
- p,
- bytes+num_bytes-p,
- &ret);
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ struct smbXcli_session *session = state->cli->smb2.session;
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
- p += ret;
+ status = smb2cli_session_set_session_key(session,
+ state->session_key,
+ state->recv_iov);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ } else {
+ struct smbXcli_session *session = state->cli->smb1.session;
+ bool active;
-next:
- if (state->blob.length != 0) {
- /*
- * More to send
- */
- if (!cli_sesssetup_blob_next(state, &subreq)) {
- tevent_req_oom(req);
+ status = smb1cli_session_set_session_key(session,
+ state->session_key);
+ if (tevent_req_nterror(req, status)) {
return;
}
- tevent_req_set_callback(subreq, cli_sesssetup_blob_done, req);
- return;
+
+ active = smb1cli_conn_activate_signing(state->cli->conn,
+ state->session_key,
+ data_blob_null);
+ if (active) {
+ bool ok;
+
+ ok = smb1cli_conn_check_signing(state->cli->conn,
+ state->inbuf, 1);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ }
}
+
tevent_req_done(req);
}
-static NTSTATUS cli_sesssetup_blob_recv(struct tevent_req *req,
- TALLOC_CTX *mem_ctx,
- DATA_BLOB *pblob,
- uint8_t **pinbuf,
- struct iovec **precv_iov)
+static NTSTATUS cli_session_setup_gensec_recv(struct tevent_req *req)
{
- struct cli_sesssetup_blob_state *state = tevent_req_data(
- req, struct cli_sesssetup_blob_state);
+ struct cli_session_setup_gensec_state *state =
+ tevent_req_data(req,
+ struct cli_session_setup_gensec_state);
NTSTATUS status;
- uint8_t *inbuf;
- struct iovec *recv_iov;
if (tevent_req_is_nterror(req, &status)) {
- TALLOC_FREE(state->cli->smb2.session);
cli_state_set_uid(state->cli, UID_FIELD_INVALID);
return status;
}
-
- inbuf = talloc_move(mem_ctx, &state->inbuf);
- recv_iov = talloc_move(mem_ctx, &state->recv_iov);
- if (pblob != NULL) {
- *pblob = state->ret_blob;
- }
- if (pinbuf != NULL) {
- *pinbuf = inbuf;
- }
- if (precv_iov != NULL) {
- *precv_iov = recv_iov;
- }
- /* could be NT_STATUS_MORE_PROCESSING_REQUIRED */
- return state->status;
+ return NT_STATUS_OK;
}
-#ifdef HAVE_KRB5
-
-/****************************************************************************
- Use in-memory credentials cache
-****************************************************************************/
+static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx,
+ const char *principal)
+{
+ char *account, *p;
-static void use_in_memory_ccache(void) {
- setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1);
+ account = talloc_strdup(mem_ctx, principal);
+ if (account == NULL) {
+ return NULL;
+ }
+ p = strchr_m(account, '@');
+ if (p != NULL) {
+ *p = '\0';
+ }
+ return account;
}
/****************************************************************************
- Do a spnego/kerberos encrypted session setup.
+ Do a spnego encrypted session setup.
+
+ user_domain: The shortname of the domain the user/machine is a member of.
+ dest_realm: The realm we're connecting to, if NULL we use our default realm.
****************************************************************************/
-struct cli_session_setup_kerberos_state {
- struct cli_state *cli;
- DATA_BLOB negTokenTarg;
- DATA_BLOB session_key_krb5;
- ADS_STATUS ads_status;
+struct cli_session_setup_spnego_state {
+ ADS_STATUS result;
};
-static void cli_session_setup_kerberos_done(struct tevent_req *subreq);
+static void cli_session_setup_spnego_done(struct tevent_req *subreq);
-static struct tevent_req *cli_session_setup_kerberos_send(
+static struct tevent_req *cli_session_setup_spnego_send(
TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
- const char *principal)
+ struct cli_credentials *creds)
{
struct tevent_req *req, *subreq;
- struct cli_session_setup_kerberos_state *state;
- int rc;
-
- DEBUG(2,("Doing kerberos session setup\n"));
+ struct cli_session_setup_spnego_state *state;
+ const char *target_service = NULL;
+ const char *target_hostname = NULL;
+ NTSTATUS status;
req = tevent_req_create(mem_ctx, &state,
- struct cli_session_setup_kerberos_state);
+ struct cli_session_setup_spnego_state);
if (req == NULL) {
return NULL;
}
- state->cli = cli;
- state->ads_status = ADS_SUCCESS;
-
- /*
- * Ok, this is cheating: spnego_gen_krb5_negTokenInit can block if
- * we have to acquire a ticket. To be fixed later :-)
- */
- rc = spnego_gen_krb5_negTokenInit(state, principal, 0, &state->negTokenTarg,
- &state->session_key_krb5, 0, NULL, NULL);
- if (rc) {
- NTSTATUS status;
-
- state->ads_status = ADS_ERROR_KRB5(rc);
- status = ads_ntstatus(state->ads_status);
- if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
- status = NT_STATUS_LOGON_FAILURE;
- state->ads_status = ADS_ERROR_NT(status);
- }
- DEBUG(1, ("cli_session_setup_kerberos: "
- "spnego_gen_krb5_negTokenInit failed: %s - %s\n",
- error_message(rc), nt_errstr(status)));
- tevent_req_nterror(req, status);
- return tevent_req_post(req, ev);
- }
-#if 0
- file_save("negTokenTarg.dat", state->negTokenTarg.data,
- state->negTokenTarg.length);
-#endif
+ target_service = "cifs";
+ target_hostname = smbXcli_conn_remote_name(cli->conn);
- if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
- state->cli->smb2.session = smbXcli_session_create(cli,
- cli->conn);
- if (tevent_req_nomem(state->cli->smb2.session, req)) {
- return tevent_req_post(req, ev);
- }
+ status = cli_session_creds_prepare_krb5(cli, creds);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
}
- subreq = cli_sesssetup_blob_send(state, ev, cli, state->negTokenTarg);
+ subreq = cli_session_setup_gensec_send(state, ev, cli, creds,
+ target_service, target_hostname);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
- tevent_req_set_callback(subreq, cli_session_setup_kerberos_done, req);
+ tevent_req_set_callback(
+ subreq, cli_session_setup_spnego_done, req);
return req;
}
-static void cli_session_setup_kerberos_done(struct tevent_req *subreq)
+static void cli_session_setup_spnego_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);
- uint8_t *inbuf = NULL;
- struct iovec *recv_iov = NULL;
NTSTATUS status;
- status = cli_sesssetup_blob_recv(subreq, state,
- NULL, &inbuf, &recv_iov);
+ status = cli_session_setup_gensec_recv(subreq);
TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
+ if (tevent_req_nterror(req, status)) {
return;
}
- if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
- struct smbXcli_session *session = state->cli->smb2.session;
- status = smb2cli_session_set_session_key(session,
- state->session_key_krb5,
- recv_iov);
- if (tevent_req_nterror(req, status)) {
- return;
- }
- } else {
- struct smbXcli_session *session = state->cli->smb1.session;
-
- status = smb1cli_session_set_session_key(session,
- state->session_key_krb5);
- if (tevent_req_nterror(req, status)) {
- return;
- }
-
- if (smb1cli_conn_activate_signing(state->cli->conn, state->session_key_krb5,
- data_blob_null)
- && !smb1cli_conn_check_signing(state->cli->conn, inbuf, 1)) {
- tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
- return;
- }
- }
-
tevent_req_done(req);
}
-static ADS_STATUS cli_session_setup_kerberos_recv(struct tevent_req *req)
+static ADS_STATUS cli_session_setup_spnego_recv(struct tevent_req *req)
{
- struct cli_session_setup_kerberos_state *state = tevent_req_data(
- req, struct cli_session_setup_kerberos_state);
+ struct cli_session_setup_spnego_state *state = tevent_req_data(
+ req, struct cli_session_setup_spnego_state);
NTSTATUS status;
if (tevent_req_is_nterror(req, &status)) {
- ADS_STATUS ads = state->ads_status;
-
- if (!ADS_ERR_OK(state->ads_status)) {
- ads = state->ads_status;
- } else {
- ads = ADS_ERROR_NT(status);
- }
- tevent_req_received(req);
- return ads;
+ state->result = ADS_ERROR_NT(status);
}
- tevent_req_received(req);
- return ADS_SUCCESS;
-}
-#endif /* HAVE_KRB5 */
-
-/****************************************************************************
- Do a spnego/NTLMSSP encrypted session setup.
-****************************************************************************/
+ return state->result;
+}
-struct cli_session_setup_gensec_state {
- struct tevent_context *ev;
+struct cli_session_setup_creds_state {
struct cli_state *cli;
- struct auth_generic_state *auth_generic;
- bool is_anonymous;
- DATA_BLOB blob_in;
- uint8_t *inbuf;
- struct iovec *recv_iov;
- DATA_BLOB blob_out;
- bool local_ready;
- bool remote_ready;
+ DATA_BLOB apassword_blob;
+ DATA_BLOB upassword_blob;
+ DATA_BLOB lm_session_key;
DATA_BLOB session_key;
+ char *out_native_os;
+ char *out_native_lm;
+ char *out_primary_domain;
};
-static int cli_session_setup_gensec_state_destructor(
- struct cli_session_setup_gensec_state *state)
+static void cli_session_setup_creds_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
{
- TALLOC_FREE(state->auth_generic);
- data_blob_clear_free(&state->session_key);
- return 0;
+ struct cli_session_setup_creds_state *state = tevent_req_data(
+ req, struct cli_session_setup_creds_state);
+
+ if (req_state != TEVENT_REQ_RECEIVED) {
+ return;
+ }
+
+ /*
+ * We only call data_blob_clear() as
+ * some of the blobs point to the same memory.
+ *
+ * We let the talloc hierachy free the memory.
+ */
+ data_blob_clear(&state->apassword_blob);
+ data_blob_clear(&state->upassword_blob);
+ data_blob_clear(&state->lm_session_key);
+ data_blob_clear(&state->session_key);
+ ZERO_STRUCTP(state);
}
-static void cli_session_setup_gensec_local_next(struct tevent_req *req);
-static void cli_session_setup_gensec_local_done(struct tevent_req *subreq);
-static void cli_session_setup_gensec_remote_next(struct tevent_req *req);
-static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq);
-static void cli_session_setup_gensec_ready(struct tevent_req *req);
+static void cli_session_setup_creds_done_spnego(struct tevent_req *subreq);
+static void cli_session_setup_creds_done_nt1(struct tevent_req *subreq);
+static void cli_session_setup_creds_done_lm21(struct tevent_req *subreq);
-static struct tevent_req *cli_session_setup_gensec_send(
- TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
- const char *user, const char *pass, const char *domain,
- enum credentials_use_kerberos krb5_state,
- const char *target_service,
- const char *target_hostname,
- const char *target_principal)
+/****************************************************************************
+ Send a session setup. The username and workgroup is in UNIX character
+ format and must be converted to DOS codepage format before sending. If the
+ password is in plaintext, the same should be done.
+****************************************************************************/
+
+struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ struct cli_credentials *creds)
{
- struct tevent_req *req;
- struct cli_session_setup_gensec_state *state;
+ struct tevent_req *req, *subreq;
+ struct cli_session_setup_creds_state *state;
+ uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
+ bool use_spnego = false;
+ int flags = 0;
+ enum credentials_use_kerberos krb5_state;
+ uint32_t gensec_features;
+ const char *username = "";
+ const char *domain = "";
+ DATA_BLOB target_info = data_blob_null;
+ DATA_BLOB challenge = data_blob_null;
+ uint16_t in_buf_size = 0;
+ uint16_t in_mpx_max = 0;
+ uint16_t in_vc_num = 0;
+ uint32_t in_sess_key = 0;
+ const char *in_native_os = NULL;
+ const char *in_native_lm = NULL;
NTSTATUS status;
- bool use_spnego_principal = lp_client_use_spnego_principal();
req = tevent_req_create(mem_ctx, &state,
- struct cli_session_setup_gensec_state);
+ struct cli_session_setup_creds_state);
if (req == NULL) {
return NULL;
}
- state->ev = ev;
state->cli = cli;
- talloc_set_destructor(
- state, cli_session_setup_gensec_state_destructor);
+ tevent_req_set_cleanup_fn(req, cli_session_setup_creds_cleanup);
- status = auth_generic_client_prepare(state, &state->auth_generic);
- if (tevent_req_nterror(req, status)) {
- return tevent_req_post(req, ev);
- }
+ krb5_state = cli_credentials_get_kerberos_state(creds);
+ gensec_features = cli_credentials_get_gensec_features(creds);
- gensec_want_feature(state->auth_generic->gensec_security,
- GENSEC_FEATURE_SESSION_KEY);
- if (cli->use_ccache) {
- gensec_want_feature(state->auth_generic->gensec_security,
- GENSEC_FEATURE_NTLM_CCACHE);
- if (pass != NULL && strlen(pass) == 0) {
- /*
- * some callers pass "" as no password
- *
- * GENSEC_FEATURE_NTLM_CCACHE only handles
- * NULL as no password.
- */
- pass = NULL;
- }
+ switch (krb5_state) {
+ case CRED_MUST_USE_KERBEROS:
+ cli->use_kerberos = true;
+ cli->fallback_after_kerberos = false;
+ break;
+ case CRED_AUTO_USE_KERBEROS:
+ cli->use_kerberos = true;
+ cli->fallback_after_kerberos = true;
+ break;
+ case CRED_DONT_USE_KERBEROS:
+ cli->use_kerberos = false;
+ cli->fallback_after_kerberos = false;
+ break;
}
- status = auth_generic_set_username(state->auth_generic, user);
- if (tevent_req_nterror(req, status)) {
- return tevent_req_post(req, ev);
+ if (gensec_features & GENSEC_FEATURE_NTLM_CCACHE) {
+ cli->use_ccache = true;
+ } else {
+ cli->use_ccache = false;
}
- status = auth_generic_set_domain(state->auth_generic, domain);
- if (tevent_req_nterror(req, status)) {
- return tevent_req_post(req, ev);
+ /*
+ * Now work out what sort of session setup we are going to
+ * do. I have split this into separate functions to make the flow a bit
+ * easier to understand (tridge).
+ */
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_NT1) {
+ use_spnego = false;
+ } else if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ use_spnego = true;
+ } else if (smb1cli_conn_capabilities(cli->conn) & CAP_EXTENDED_SECURITY) {
+ /*
+ * if the server supports extended security then use SPNEGO
+ * even for anonymous connections.
+ */
+ use_spnego = true;
+ } else {
+ use_spnego = false;
}
- if (cli->pw_nt_hash) {
- struct samr_Password nt_hash;
- size_t converted;
- bool ok;
-
- converted = strhex_to_str((char *)nt_hash.hash,
- sizeof(nt_hash.hash),
- pass, strlen(pass));
- if (converted != sizeof(nt_hash.hash)) {
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ if (use_spnego) {
+ subreq = cli_session_setup_spnego_send(
+ state, ev, cli, creds);
+ if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
+ tevent_req_set_callback(subreq, cli_session_setup_creds_done_spnego,
+ req);
+ return req;
+ }
- ok = cli_credentials_set_nt_hash(state->auth_generic->credentials,
- &nt_hash, CRED_SPECIFIED);
- if (!ok) {
- tevent_req_oom(req);
- return tevent_req_post(req, ev);
- }
- } else {
- status = auth_generic_set_password(state->auth_generic, pass);
- if (tevent_req_nterror(req, status)) {
- return tevent_req_post(req, ev);
- }
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN1) {
+ /*
+ * SessionSetupAndX was introduced by LANMAN 1.0. So we skip
+ * this step against older servers.
+ */
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
}
- cli_credentials_set_kerberos_state(state->auth_generic->credentials,
- krb5_state);
+ if (cli_credentials_is_anonymous(creds)) {
+ /*
+ * Do an anonymous session setup
+ */
+ goto non_spnego_creds_done;
+ }
- if (krb5_state == CRED_DONT_USE_KERBEROS) {
- use_spnego_principal = false;
+ if ((sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) == 0) {
+ /*
+ * Do an anonymous session setup,
+ * the password is passed via the tree connect.
+ */
+ goto non_spnego_creds_done;
}
- if (target_service != NULL) {
- status = gensec_set_target_service(
- state->auth_generic->gensec_security,
- target_service);
- if (tevent_req_nterror(req, status)) {
- return tevent_req_post(req, ev);
- }
+ cli_credentials_get_ntlm_username_domain(creds, state,
+ &username,
+ &domain);
+ if (tevent_req_nomem(username, req)) {
+ return tevent_req_post(req, ev);
+ }
+ if (tevent_req_nomem(domain, req)) {
+ return tevent_req_post(req, ev);
}
- if (target_hostname != NULL) {
- status = gensec_set_target_hostname(
- state->auth_generic->gensec_security,
- target_hostname);
- if (tevent_req_nterror(req, status)) {
- return tevent_req_post(req, ev);
+ if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+ bool use_unicode = smbXcli_conn_use_unicode(cli->conn);
+ uint8_t *bytes = NULL;
+ size_t bytes_len = 0;
+ const char *pw = cli_credentials_get_password(creds);
+ size_t pw_len = 0;
+
+ if (pw == NULL) {
+ pw = "";
}
- }
+ pw_len = strlen(pw) + 1;
- if (target_principal != NULL) {
- status = gensec_set_target_principal(
- state->auth_generic->gensec_security,
- target_principal);
- if (tevent_req_nterror(req, status)) {
+ if (!lp_client_plaintext_auth()) {
+ DEBUG(1, ("Server requested PLAINTEXT password but "
+ "'client plaintext auth = no'\n"));
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
return tevent_req_post(req, ev);
}
- use_spnego_principal = false;
- } else if (target_service != NULL && target_hostname != NULL) {
- use_spnego_principal = false;
- }
- if (use_spnego_principal) {
- const DATA_BLOB *b;
- b = smbXcli_conn_server_gss_blob(cli->conn);
- if (b != NULL) {
- state->blob_in = *b;
+ bytes = talloc_array(state, uint8_t, 0);
+ bytes = trans2_bytes_push_str(bytes, use_unicode,
+ pw, pw_len, &bytes_len);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
}
- }
- state->is_anonymous = cli_credentials_is_anonymous(state->auth_generic->credentials);
+ if (use_unicode) {
+ /*
+ * CAP_UNICODE, can only be negotiated by NT1.
+ */
+ state->upassword_blob = data_blob_const(bytes,
+ bytes_len);
+ } else {
+ state->apassword_blob = data_blob_const(bytes,
+ bytes_len);
+ }
- status = auth_generic_client_start(state->auth_generic,
- GENSEC_OID_SPNEGO);
- if (tevent_req_nterror(req, status)) {
- return tevent_req_post(req, ev);
+ goto non_spnego_creds_done;
}
- if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
- state->cli->smb2.session = smbXcli_session_create(cli,
- cli->conn);
- if (tevent_req_nomem(state->cli->smb2.session, req)) {
+ challenge = data_blob_const(smb1cli_conn_server_challenge(cli->conn), 8);
+
+ if (smbXcli_conn_protocol(cli->conn) == PROTOCOL_NT1) {
+ if (lp_client_ntlmv2_auth() && lp_client_use_spnego()) {
+ /*
+ * Don't send an NTLMv2 response without NTLMSSP if we
+ * want to use spnego support.
+ */
+ DEBUG(1, ("Server does not support EXTENDED_SECURITY "
+ " but 'client use spnego = yes'"
+ " and 'client ntlmv2 auth = yes' is set\n"));
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
return tevent_req_post(req, ev);
}
- }
-
- cli_session_setup_gensec_local_next(req);
- if (!tevent_req_is_in_progress(req)) {
- return tevent_req_post(req, ev);
- }
-
- return req;
-}
-static void cli_session_setup_gensec_local_next(struct tevent_req *req)
-{
- struct cli_session_setup_gensec_state *state =
- tevent_req_data(req,
- struct cli_session_setup_gensec_state);
- struct tevent_req *subreq = NULL;
+ if (lp_client_ntlmv2_auth()) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
- if (state->local_ready) {
- tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
- return;
- }
+ /*
+ * note that the 'domain' here is a best
+ * guess - we don't know the server's domain
+ * at this point. Windows clients also don't
+ * use hostname...
+ */
+ target_info = NTLMv2_generate_names_blob(state,
+ NULL,
+ domain);
+ if (tevent_req_nomem(target_info.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ flags |= CLI_CRED_NTLM_AUTH;
+ if (lp_client_lanman_auth()) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+ }
+ } else {
+ if (!lp_client_lanman_auth()) {
+ DEBUG(1, ("Server requested user level LM password but "
+ "'client lanman auth = no' is set.\n"));
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return tevent_req_post(req, ev);
+ }
- subreq = gensec_update_send(state, state->ev,
- state->auth_generic->gensec_security,
- state->blob_in);
- if (tevent_req_nomem(subreq, req)) {
- return;
+ flags |= CLI_CRED_LANMAN_AUTH;
}
- tevent_req_set_callback(subreq, cli_session_setup_gensec_local_done, req);
-}
-
-static void cli_session_setup_gensec_local_done(struct tevent_req *subreq)
-{
- struct tevent_req *req =
- tevent_req_callback_data(subreq,
- struct tevent_req);
- struct cli_session_setup_gensec_state *state =
- tevent_req_data(req,
- struct cli_session_setup_gensec_state);
- NTSTATUS status;
- status = gensec_update_recv(subreq, state, &state->blob_out);
- TALLOC_FREE(subreq);
- state->blob_in = data_blob_null;
- if (!NT_STATUS_IS_OK(status) &&
- !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
- {
- tevent_req_nterror(req, status);
- return;
+ status = cli_credentials_get_ntlm_response(creds, state, &flags,
+ challenge, NULL,
+ target_info,
+ &state->apassword_blob,
+ &state->upassword_blob,
+ &state->lm_session_key,
+ &state->session_key);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
}
- if (NT_STATUS_IS_OK(status)) {
- state->local_ready = true;
- }
+non_spnego_creds_done:
- if (state->local_ready && state->remote_ready) {
- cli_session_setup_gensec_ready(req);
- return;
- }
+ in_buf_size = CLI_BUFFER_SIZE;
+ in_mpx_max = smbXcli_conn_max_requests(cli->conn);
+ in_vc_num = cli_state_get_vc_num(cli);
+ in_sess_key = smb1cli_conn_server_session_key(cli->conn);
+ in_native_os = "Unix";
+ in_native_lm = "Samba";
- cli_session_setup_gensec_remote_next(req);
-}
+ if (smbXcli_conn_protocol(cli->conn) == PROTOCOL_NT1) {
+ uint32_t in_capabilities = 0;
-static void cli_session_setup_gensec_remote_next(struct tevent_req *req)
-{
- struct cli_session_setup_gensec_state *state =
- tevent_req_data(req,
- struct cli_session_setup_gensec_state);
- struct tevent_req *subreq = NULL;
+ in_capabilities = cli_session_setup_capabilities(cli, 0);
- if (state->remote_ready) {
- tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
- return;
+ /*
+ * For now we keep the same values as before,
+ * we may remove these in a separate commit later.
+ */
+ in_mpx_max = 2;
+
+ subreq = smb1cli_session_setup_nt1_send(state, ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.session,
+ in_buf_size,
+ in_mpx_max,
+ in_vc_num,
+ in_sess_key,
+ username,
+ domain,
+ state->apassword_blob,
+ state->upassword_blob,
+ in_capabilities,
+ in_native_os,
+ in_native_lm);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_session_setup_creds_done_nt1,
+ req);
+ return req;
}
- subreq = cli_sesssetup_blob_send(state, state->ev,
- state->cli, state->blob_out);
+ /*
+ * For now we keep the same values as before,
+ * we may remove these in a separate commit later.
+ */
+ in_mpx_max = 2;
+ in_vc_num = 1;
+
+ subreq = smb1cli_session_setup_lm21_send(state, ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.session,
+ in_buf_size,
+ in_mpx_max,
+ in_vc_num,
+ in_sess_key,
+ username,
+ domain,
+ state->apassword_blob,
+ in_native_os,
+ in_native_lm);
if (tevent_req_nomem(subreq, req)) {
- return;
+ return tevent_req_post(req, ev);
}
- tevent_req_set_callback(subreq,
- cli_session_setup_gensec_remote_done,
+ tevent_req_set_callback(subreq, cli_session_setup_creds_done_lm21,
req);
+ return req;
}
-static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq)
+static void cli_session_setup_creds_done_spnego(struct tevent_req *subreq)
{
- struct tevent_req *req =
- tevent_req_callback_data(subreq,
- struct tevent_req);
- struct cli_session_setup_gensec_state *state =
- tevent_req_data(req,
- struct cli_session_setup_gensec_state);
- NTSTATUS status;
-
- TALLOC_FREE(state->inbuf);
- TALLOC_FREE(state->recv_iov);
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ ADS_STATUS status;
- status = cli_sesssetup_blob_recv(subreq, state, &state->blob_in,
- &state->inbuf, &state->recv_iov);
+ status = cli_session_setup_spnego_recv(subreq);
TALLOC_FREE(subreq);
- data_blob_free(&state->blob_out);
- if (!NT_STATUS_IS_OK(status) &&
- !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
- {
- tevent_req_nterror(req, status);
- return;
- }
-
- if (NT_STATUS_IS_OK(status)) {
- state->remote_ready = true;
- }
-
- if (state->local_ready && state->remote_ready) {
- cli_session_setup_gensec_ready(req);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
+ tevent_req_nterror(req, ads_ntstatus(status));
return;
}
-
- cli_session_setup_gensec_local_next(req);
+ tevent_req_done(req);
}
-static void cli_session_setup_gensec_ready(struct tevent_req *req)
+static void cli_session_setup_creds_done_nt1(struct tevent_req *subreq)
{
- struct cli_session_setup_gensec_state *state =
- tevent_req_data(req,
- struct cli_session_setup_gensec_state);
- const char *server_domain = NULL;
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_session_setup_creds_state *state = tevent_req_data(
+ req, struct cli_session_setup_creds_state);
+ struct cli_state *cli = state->cli;
NTSTATUS status;
-
- if (state->blob_in.length != 0) {
- tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
- return;
- }
-
- if (state->blob_out.length != 0) {
- tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ struct iovec *recv_iov = NULL;
+ const uint8_t *inbuf = NULL;
+ bool ok;
+
+ status = smb1cli_session_setup_nt1_recv(subreq, state,
+ &recv_iov,
+ &inbuf,
+ &state->out_native_os,
+ &state->out_native_lm,
+ &state->out_primary_domain);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("NT1 login failed: %s\n", nt_errstr(status)));
+ tevent_req_nterror(req, status);
return;
}
- /*
- * gensec_ntlmssp_server_domain() returns NULL
- * if NTLMSSP is not used.
- *
- * We can remove this later
- * and leave the server domain empty for SMB2 and above
- * in future releases.
- */
- server_domain = gensec_ntlmssp_server_domain(
- state->auth_generic->gensec_security);
-
- if (state->cli->server_domain[0] == '\0' && server_domain != NULL) {
- TALLOC_FREE(state->cli->server_domain);
- state->cli->server_domain = talloc_strdup(state->cli,
- server_domain);
- if (state->cli->server_domain == NULL) {
- tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
- return;
- }
- }
-
- status = gensec_session_key(state->auth_generic->gensec_security,
- state, &state->session_key);
+ status = cli_state_update_after_sesssetup(state->cli,
+ state->out_native_os,
+ state->out_native_lm,
+ state->out_primary_domain);
if (tevent_req_nterror(req, status)) {
return;
}
- if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
- struct smbXcli_session *session = state->cli->smb2.session;
-
- if (state->is_anonymous) {
- /*
- * Windows server does not set the
- * SMB2_SESSION_FLAG_IS_GUEST nor
- * SMB2_SESSION_FLAG_IS_NULL flag.
- *
- * This fix makes sure we do not try
- * to verify a signature on the final
- * session setup response.
- */
- tevent_req_done(req);
+ ok = smb1cli_conn_activate_signing(cli->conn,
+ state->session_key,
+ state->upassword_blob);
+ if (ok) {
+ ok = smb1cli_conn_check_signing(cli->conn, inbuf, 1);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
return;
}
+ }
- status = smb2cli_session_set_session_key(session,
- state->session_key,
- state->recv_iov);
- if (tevent_req_nterror(req, status)) {
- return;
- }
- } else {
- struct smbXcli_session *session = state->cli->smb1.session;
- bool active;
+ if (state->session_key.data) {
+ struct smbXcli_session *session = cli->smb1.session;
status = smb1cli_session_set_session_key(session,
state->session_key);
if (tevent_req_nterror(req, status)) {
return;
}
-
- active = smb1cli_conn_activate_signing(state->cli->conn,
- state->session_key,
- data_blob_null);
- if (active) {
- bool ok;
-
- ok = smb1cli_conn_check_signing(state->cli->conn,
- state->inbuf, 1);
- if (!ok) {
- tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
- return;
- }
- }
}
tevent_req_done(req);
}
-static NTSTATUS cli_session_setup_gensec_recv(struct tevent_req *req)
+static void cli_session_setup_creds_done_lm21(struct tevent_req *subreq)
{
- struct cli_session_setup_gensec_state *state =
- tevent_req_data(req,
- struct cli_session_setup_gensec_state);
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_session_setup_creds_state *state = tevent_req_data(
+ req, struct cli_session_setup_creds_state);
NTSTATUS status;
- if (tevent_req_is_nterror(req, &status)) {
- cli_state_set_uid(state->cli, UID_FIELD_INVALID);
- return status;
- }
- return NT_STATUS_OK;
-}
-
-#ifdef HAVE_KRB5
-
-static char *cli_session_setup_get_principal(
- TALLOC_CTX *mem_ctx, const char *spnego_principal,
- const char *remote_name, const char *dest_realm)
-{
- char *principal = NULL;
-
- if (!lp_client_use_spnego_principal() ||
- strequal(spnego_principal, ADS_IGNORE_PRINCIPAL)) {
- spnego_principal = NULL;
- }
- if (spnego_principal != NULL) {
- DEBUG(3, ("cli_session_setup_spnego: using spnego provided "
- "principal %s\n", spnego_principal));
- return talloc_strdup(mem_ctx, spnego_principal);
- }
- if (is_ipaddress(remote_name) ||
- strequal(remote_name, STAR_SMBSERVER)) {
- return NULL;
+ status = smb1cli_session_setup_lm21_recv(subreq, state,
+ &state->out_native_os,
+ &state->out_native_lm);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("LM21 login failed: %s\n", nt_errstr(status)));
+ tevent_req_nterror(req, status);
+ return;
}
- DEBUG(3, ("cli_session_setup_spnego: using target "
- "hostname not SPNEGO principal\n"));
-
- if (dest_realm) {
- char *realm = strupper_talloc(talloc_tos(), dest_realm);
- if (realm == NULL) {
- return NULL;
- }
- principal = talloc_asprintf(talloc_tos(), "cifs/%s@%s",
- remote_name, realm);
- TALLOC_FREE(realm);
- } else {
- principal = kerberos_get_principal_from_service_hostname(
- talloc_tos(), "cifs", remote_name, lp_realm());
+ status = cli_state_update_after_sesssetup(state->cli,
+ state->out_native_os,
+ state->out_native_lm,
+ NULL);
+ if (tevent_req_nterror(req, status)) {
+ return;
}
- DEBUG(3, ("cli_session_setup_spnego: guessed server principal=%s\n",
- principal ? principal : "<null>"));
- return principal;
+ tevent_req_done(req);
}
-#endif
-static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx,
- const char *principal)
+NTSTATUS cli_session_setup_creds_recv(struct tevent_req *req)
{
- char *account, *p;
-
- account = talloc_strdup(mem_ctx, principal);
- if (account == NULL) {
- return NULL;
- }
- p = strchr_m(account, '@');
- if (p != NULL) {
- *p = '\0';
- }
- return account;
+ return tevent_req_simple_recv_ntstatus(req);
}
-/****************************************************************************
- Do a spnego encrypted session setup.
-
- user_domain: The shortname of the domain the user/machine is a member of.
- dest_realm: The realm we're connecting to, if NULL we use our default realm.
-****************************************************************************/
-
-struct cli_session_setup_spnego_state {
- struct tevent_context *ev;
- struct cli_state *cli;
- const char *target_hostname;
- const char *user;
- const char *account;
- const char *pass;
- const char *user_domain;
- const char *dest_realm;
- ADS_STATUS result;
-};
-
-#ifdef HAVE_KRB5
-static void cli_session_setup_spnego_done_krb(struct tevent_req *subreq);
-#endif
-
-static void cli_session_setup_spnego_done_ntlmssp(struct tevent_req *subreq);
-
-static struct tevent_req *cli_session_setup_spnego_send(
- TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
- const char *user, const char *pass, const char *user_domain)
+NTSTATUS cli_session_setup_creds(struct cli_state *cli,
+ struct cli_credentials *creds)
{
- struct tevent_req *req, *subreq;
- struct cli_session_setup_spnego_state *state;
- char *principal = NULL;
- char *OIDs[ASN1_MAX_OIDS];
- int i;
- const char *dest_realm = cli_state_remote_realm(cli);
- const DATA_BLOB *server_blob;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
- req = tevent_req_create(mem_ctx, &state,
- struct cli_session_setup_spnego_state);
- if (req == NULL) {
- return NULL;
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ return NT_STATUS_INVALID_PARAMETER;
}
- state->ev = ev;
- state->cli = cli;
- state->user = user;
- state->pass = pass;
- state->user_domain = user_domain;
- state->dest_realm = dest_realm;
-
- state->account = cli_session_setup_get_account(state, user);
- if (tevent_req_nomem(state->account, req)) {
- return tevent_req_post(req, ev);
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_session_setup_creds_send(ev, ev, cli, creds);
+ if (req == NULL) {
+ goto fail;
}
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_session_setup_creds_recv(req);
+ fail:
+ TALLOC_FREE(ev);
+ return status;
+}
- state->target_hostname = smbXcli_conn_remote_name(cli->conn);
- server_blob = smbXcli_conn_server_gss_blob(cli->conn);
-
- DEBUG(3,("Doing spnego session setup (blob length=%lu)\n",
- (unsigned long)server_blob->length));
+NTSTATUS cli_session_setup_anon(struct cli_state *cli)
+{
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ struct cli_credentials *creds = NULL;
- /* the server might not even do spnego */
- if (server_blob->length == 0) {
- DEBUG(3,("server didn't supply a full spnego negprot\n"));
- goto ntlmssp;
- }
-
-#if 0
- file_save("negprot.dat", cli->secblob.data, cli->secblob.length);
-#endif
-
- /* The server sent us the first part of the SPNEGO exchange in the
- * negprot reply. It is WRONG to depend on the principal sent in the
- * negprot reply, but right now we do it. If we don't receive one,
- * we try to best guess, then fall back to NTLM. */
- if (!spnego_parse_negTokenInit(state, *server_blob, OIDs,
- &principal, NULL) ||
- OIDs[0] == NULL) {
- state->result = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
- tevent_req_done(req);
- return tevent_req_post(req, ev);
+ creds = cli_credentials_init_anon(cli);
+ if (creds == NULL) {
+ return NT_STATUS_NO_MEMORY;
}
- /* make sure the server understands kerberos */
- for (i=0;OIDs[i];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;
- }
- talloc_free(OIDs[i]);
+ status = cli_session_setup_creds(cli, creds);
+ TALLOC_FREE(creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
- DEBUG(3,("got principal=%s\n", principal ? principal : "<null>"));
-
-#ifdef HAVE_KRB5
- /* If password is set we reauthenticate to kerberos server
- * and do not store results */
-
- if (user && *user && cli->got_kerberos_mechanism && cli->use_kerberos) {
- char *tmp;
+ return NT_STATUS_OK;
+}
- tmp = cli_session_setup_get_principal(
- talloc_tos(), principal, state->target_hostname, dest_realm);
- TALLOC_FREE(principal);
- principal = tmp;
+/****************************************************************************
+ Send a uloggoff.
+*****************************************************************************/
- if (pass && *pass) {
- int ret;
+struct cli_ulogoff_state {
+ struct cli_state *cli;
+ uint16_t vwv[3];
+};
- use_in_memory_ccache();
- ret = kerberos_kinit_password(user, pass, 0 /* no time correction for now */, NULL);
+static void cli_ulogoff_done(struct tevent_req *subreq);
- if (ret){
- DEBUG(0, ("Kinit for %s to access %s failed: %s\n", user, principal, error_message(ret)));
- TALLOC_FREE(principal);
- if (cli->fallback_after_kerberos)
- goto ntlmssp;
- state->result = ADS_ERROR_KRB5(ret);
- tevent_req_done(req);
- return tevent_req_post(req, ev);
- }
- }
+static struct tevent_req *cli_ulogoff_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_ulogoff_state *state;
- if (principal) {
- subreq = cli_session_setup_kerberos_send(
- state, ev, cli, principal);
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
- }
- tevent_req_set_callback(
- subreq, cli_session_setup_spnego_done_krb,
- req);
- return req;
- }
+ req = tevent_req_create(mem_ctx, &state, struct cli_ulogoff_state);
+ if (req == NULL) {
+ return NULL;
}
-#endif
+ state->cli = cli;
+
+ SCVAL(state->vwv+0, 0, 0xFF);
+ SCVAL(state->vwv+1, 0, 0);
+ SSVAL(state->vwv+2, 0, 0);
-ntlmssp:
- subreq = cli_session_setup_gensec_send(
- state, state->ev, state->cli,
- state->account, state->pass, state->user_domain,
- CRED_DONT_USE_KERBEROS,
- "cifs", state->target_hostname, NULL);
+ subreq = cli_smb_send(state, ev, cli, SMBulogoffX, 0, 0, 2, state->vwv,
+ 0, NULL);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
- tevent_req_set_callback(
- subreq, cli_session_setup_spnego_done_ntlmssp, req);
+ tevent_req_set_callback(subreq, cli_ulogoff_done, req);
return req;
}
-#ifdef HAVE_KRB5
-static void cli_session_setup_spnego_done_krb(struct tevent_req *subreq)
+static void cli_ulogoff_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
- struct cli_session_setup_spnego_state *state = tevent_req_data(
- req, struct cli_session_setup_spnego_state);
-
- state->result = cli_session_setup_kerberos_recv(subreq);
- TALLOC_FREE(subreq);
-
- if (ADS_ERR_OK(state->result) ||
- !state->cli->fallback_after_kerberos) {
- tevent_req_done(req);
- return;
- }
+ struct cli_ulogoff_state *state = tevent_req_data(
+ req, struct cli_ulogoff_state);
+ NTSTATUS status;
- subreq = cli_session_setup_gensec_send(
- state, state->ev, state->cli,
- state->account, state->pass, state->user_domain,
- CRED_DONT_USE_KERBEROS,
- "cifs", state->target_hostname, NULL);
- if (tevent_req_nomem(subreq, req)) {
+ status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
return;
}
- tevent_req_set_callback(subreq, cli_session_setup_spnego_done_ntlmssp,
- req);
+ cli_state_set_uid(state->cli, UID_FIELD_INVALID);
+ tevent_req_done(req);
}
-#endif
-static void cli_session_setup_spnego_done_ntlmssp(struct tevent_req *subreq)
+static NTSTATUS cli_ulogoff_recv(struct tevent_req *req)
{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct cli_session_setup_spnego_state *state = tevent_req_data(
- req, struct cli_session_setup_spnego_state);
- NTSTATUS status;
-
- status = cli_session_setup_gensec_recv(subreq);
- TALLOC_FREE(subreq);
- state->result = ADS_ERROR_NT(status);
- tevent_req_done(req);
+ return tevent_req_simple_recv_ntstatus(req);
}
-static ADS_STATUS cli_session_setup_spnego_recv(struct tevent_req *req)
+NTSTATUS cli_ulogoff(struct cli_state *cli)
{
- struct cli_session_setup_spnego_state *state = tevent_req_data(
- req, struct cli_session_setup_spnego_state);
-
- return state->result;
-}
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
-struct cli_session_setup_state {
- uint8_t dummy;
-};
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ status = smb2cli_logoff(cli->conn,
+ cli->timeout,
+ cli->smb2.session);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ smb2cli_session_set_id_and_flags(cli->smb2.session,
+ UINT64_MAX, 0);
+ return NT_STATUS_OK;
+ }
-static void cli_session_setup_done_lanman2(struct tevent_req *subreq);
-static void cli_session_setup_done_spnego(struct tevent_req *subreq);
-static void cli_session_setup_done_guest(struct tevent_req *subreq);
-static void cli_session_setup_done_plain(struct tevent_req *subreq);
-static void cli_session_setup_done_nt1(struct tevent_req *subreq);
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ev = samba_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);
+ return status;
+}
/****************************************************************************
- Send a session setup. The username and workgroup is in UNIX character
- format and must be converted to DOS codepage format before sending. If the
- password is in plaintext, the same should be done.
+ Send a tconX.
****************************************************************************/
-struct tevent_req *cli_session_setup_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct cli_state *cli,
- const char *user,
- const char *pass, int passlen,
- const char *ntpass, int ntpasslen,
- const char *workgroup)
+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_create(TALLOC_CTX *mem_ctx,
+ struct tevent_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_session_setup_state *state;
- char *p;
- char *user2;
+ struct cli_tcon_andx_state *state;
+ uint8_t p24[24];
+ uint16_t *vwv;
+ char *tmp = NULL;
+ uint8_t *bytes;
uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
+ uint16_t tcon_flags = 0;
- req = tevent_req_create(mem_ctx, &state,
- struct cli_session_setup_state);
+ *psmbreq = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_tcon_andx_state);
if (req == NULL) {
return NULL;
}
+ state->cli = cli;
+ vwv = state->vwv;
- if (user) {
- user2 = talloc_strdup(state, user);
- } else {
- user2 = talloc_strdup(state, "");
- }
- if (user2 == NULL) {
- tevent_req_oom(req);
+ TALLOC_FREE(cli->smb1.tcon);
+ cli->smb1.tcon = smbXcli_tcon_create(cli);
+ if (tevent_req_nomem(cli->smb1.tcon, req)) {
return tevent_req_post(req, ev);
}
+ smb1cli_tcon_set_id(cli->smb1.tcon, UINT16_MAX);
- if (!workgroup) {
- workgroup = "";
- }
-
- /* allow for workgroups as part of the username */
- if ((p=strchr_m(user2,'\\')) || (p=strchr_m(user2,'/')) ||
- (p=strchr_m(user2,*lp_winbind_separator()))) {
- *p = 0;
- user = p+1;
- if (!strupper_m(user2)) {
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return tevent_req_post(req, ev);
- }
- workgroup = user2;
- }
-
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN1) {
- tevent_req_done(req);
- return tevent_req_post(req, ev);
+ cli->share = talloc_strdup(cli, share);
+ if (!cli->share) {
+ return NULL;
}
- /* now work out what sort of session setup we are going to
- do. I have split this into separate functions to make the
- flow a bit easier to understand (tridge) */
-
- /* if its an older server then we have to use the older request format */
-
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_NT1) {
- if (!lp_client_lanman_auth() && passlen != 24 && (*pass)) {
- DEBUG(1, ("Server requested LM password but 'client lanman auth = no'"
- " or 'client ntlmv2 auth = yes'\n"));
- tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
- return tevent_req_post(req, ev);
- }
-
- if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0 &&
- !lp_client_plaintext_auth() && (*pass)) {
- DEBUG(1, ("Server requested PLAINTEXT password but 'client plaintext auth = no'"
- " or 'client ntlmv2 auth = yes'\n"));
- tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
- return tevent_req_post(req, ev);
- }
-
- subreq = cli_session_setup_lanman2_send(
- state, ev, cli, user, pass, passlen, workgroup);
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
- }
- tevent_req_set_callback(subreq, cli_session_setup_done_lanman2,
- req);
- return req;
+ /* in user level security don't send a password now */
+ if (sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) {
+ passlen = 1;
+ pass = "";
+ } else if (pass == NULL) {
+ DEBUG(1, ("Server not using user level security and no "
+ "password supplied.\n"));
+ goto access_denied;
}
- if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
- subreq = cli_session_setup_spnego_send(
- state, ev, cli, user, pass, workgroup);
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
+ if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) &&
+ *pass && passlen != 24) {
+ if (!lp_client_lanman_auth()) {
+ DEBUG(1, ("Server requested LANMAN password "
+ "(share-level security) but "
+ "'client lanman auth = no' or 'client ntlmv2 auth = yes'\n"));
+ goto access_denied;
}
- tevent_req_set_callback(subreq, cli_session_setup_done_spnego,
- req);
- return req;
- }
-
- /* if no user is supplied then we have to do an anonymous connection.
- passwords are ignored */
- if (!user || !*user) {
- subreq = cli_session_setup_guest_send(state, ev, cli);
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
- }
- tevent_req_set_callback(subreq, cli_session_setup_done_guest,
- req);
- return req;
- }
+ /*
+ * Non-encrypted passwords - convert to DOS codepage before
+ * encryption.
+ */
+ SMBencrypt(pass, smb1cli_conn_server_challenge(cli->conn), p24);
+ passlen = 24;
+ pass = (const char *)p24;
+ } else {
+ if((sec_mode & (NEGOTIATE_SECURITY_USER_LEVEL
+ |NEGOTIATE_SECURITY_CHALLENGE_RESPONSE))
+ == 0) {
+ uint8_t *tmp_pass;
- /* if the server is share level then send a plaintext null
- password at this point. The password is sent in the tree
- connect */
+ if (!lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested PLAINTEXT "
+ "password but "
+ "'client plaintext auth = no' or 'client ntlmv2 auth = yes'\n"));
+ goto access_denied;
+ }
- if ((sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) == 0) {
- subreq = cli_session_setup_plain_send(
- state, ev, cli, user, "", workgroup);
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
+ /*
+ * Non-encrypted passwords - convert to DOS codepage
+ * before using.
+ */
+ tmp_pass = talloc_array(talloc_tos(), uint8_t, 0);
+ if (tevent_req_nomem(tmp_pass, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tmp_pass = trans2_bytes_push_str(tmp_pass,
+ false, /* always DOS */
+ pass,
+ passlen,
+ NULL);
+ if (tevent_req_nomem(tmp_pass, req)) {
+ return tevent_req_post(req, ev);
+ }
+ pass = (const char *)tmp_pass;
+ passlen = talloc_get_size(tmp_pass);
}
- tevent_req_set_callback(subreq, cli_session_setup_done_plain,
- req);
- return req;
}
- /* if the server doesn't support encryption then we have to use
- plaintext. The second password is ignored */
-
- if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
- if (!lp_client_plaintext_auth() && (*pass)) {
- DEBUG(1, ("Server requested PLAINTEXT password but 'client plaintext auth = no'"
- " or 'client ntlmv2 auth = yes'\n"));
- tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
- return tevent_req_post(req, ev);
- }
- subreq = cli_session_setup_plain_send(
- state, ev, cli, user, pass, workgroup);
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
- }
- tevent_req_set_callback(subreq, cli_session_setup_done_plain,
- req);
- return req;
- }
+ tcon_flags |= TCONX_FLAG_EXTENDED_RESPONSE;
+ tcon_flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
- /* if the server supports extended security then use SPNEGO */
+ SCVAL(vwv+0, 0, 0xFF);
+ SCVAL(vwv+0, 1, 0);
+ SSVAL(vwv+1, 0, 0);
+ SSVAL(vwv+2, 0, tcon_flags);
+ SSVAL(vwv+3, 0, passlen);
- if (smb1cli_conn_capabilities(cli->conn) & CAP_EXTENDED_SECURITY) {
- subreq = cli_session_setup_spnego_send(
- state, ev, cli, user, pass, workgroup);
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
- }
- tevent_req_set_callback(subreq, cli_session_setup_done_spnego,
- req);
- return req;
+ if (passlen && pass) {
+ bytes = (uint8_t *)talloc_memdup(state, pass, passlen);
} else {
- /* otherwise do a NT1 style session setup */
-
- subreq = cli_session_setup_nt1_send(
- state, ev, cli, user, pass, passlen, ntpass, ntpasslen,
- workgroup);
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
- }
- tevent_req_set_callback(subreq, cli_session_setup_done_nt1,
- req);
- return req;
+ bytes = talloc_array(state, uint8_t, 0);
}
- tevent_req_done(req);
- return tevent_req_post(req, ev);
-}
-
-static void cli_session_setup_done_lanman2(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- NTSTATUS status;
-
- status = cli_session_setup_lanman2_recv(subreq);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
+ /*
+ * Add the sharename
+ */
+ tmp = talloc_asprintf_strupper_m(talloc_tos(), "\\\\%s\\%s",
+ smbXcli_conn_remote_name(cli->conn), share);
+ if (tmp == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
}
- tevent_req_done(req);
-}
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), tmp, strlen(tmp)+1,
+ NULL);
+ TALLOC_FREE(tmp);
-static void cli_session_setup_done_spnego(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- ADS_STATUS status;
+ /*
+ * Add the devicetype
+ */
+ tmp = talloc_strdup_upper(talloc_tos(), dev);
+ if (tmp == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ bytes = smb_bytes_push_str(bytes, false, tmp, strlen(tmp)+1, NULL);
+ TALLOC_FREE(tmp);
- status = cli_session_setup_spnego_recv(subreq);
- TALLOC_FREE(subreq);
- if (!ADS_ERR_OK(status)) {
- DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
- tevent_req_nterror(req, ads_ntstatus(status));
- return;
+ if (bytes == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
}
- tevent_req_done(req);
-}
-static void cli_session_setup_done_guest(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- NTSTATUS status;
+ state->bytes.iov_base = (void *)bytes;
+ state->bytes.iov_len = talloc_get_size(bytes);
- status = cli_session_setup_guest_recv(subreq);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
+ subreq = cli_smb_req_create(state, ev, cli, SMBtconX, 0, 0, 4, vwv,
+ 1, &state->bytes);
+ if (subreq == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
}
- tevent_req_done(req);
+ tevent_req_set_callback(subreq, cli_tcon_andx_done, req);
+ *psmbreq = subreq;
+ return req;
+
+ access_denied:
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return tevent_req_post(req, ev);
}
-static void cli_session_setup_done_plain(struct tevent_req *subreq)
+struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *share, const char *dev,
+ const char *pass, int passlen)
{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
+ struct tevent_req *req, *subreq;
NTSTATUS status;
- status = cli_session_setup_plain_recv(subreq);
- TALLOC_FREE(subreq);
+ 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 = smb1cli_req_chain_submit(&subreq, 1);
if (!NT_STATUS_IS_OK(status)) {
tevent_req_nterror(req, status);
- return;
+ return tevent_req_post(req, ev);
}
- tevent_req_done(req);
+ return req;
}
-static void cli_session_setup_done_nt1(struct tevent_req *subreq)
+static void cli_tcon_andx_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
+ struct cli_tcon_andx_state *state = tevent_req_data(
+ req, struct cli_tcon_andx_state);
+ struct cli_state *cli = state->cli;
+ uint8_t *in;
+ uint8_t *inhdr;
+ uint8_t wct;
+ uint16_t *vwv;
+ uint32_t num_bytes;
+ uint8_t *bytes;
NTSTATUS status;
+ uint16_t optional_support = 0;
- status = cli_session_setup_nt1_recv(subreq);
+ status = cli_smb_recv(subreq, state, &in, 0, &wct, &vwv,
+ &num_bytes, &bytes);
TALLOC_FREE(subreq);
if (!NT_STATUS_IS_OK(status)) {
- DEBUG(3, ("cli_session_setup: NT1 session setup "
- "failed: %s\n", nt_errstr(status)));
tevent_req_nterror(req, status);
return;
}
- tevent_req_done(req);
-}
-
-NTSTATUS cli_session_setup_recv(struct tevent_req *req)
-{
- return tevent_req_simple_recv_ntstatus(req);
-}
-NTSTATUS cli_session_setup(struct cli_state *cli,
- const char *user,
- const char *pass, int passlen,
- const char *ntpass, int ntpasslen,
- const char *workgroup)
-{
- struct tevent_context *ev;
- struct tevent_req *req;
- NTSTATUS status = NT_STATUS_NO_MEMORY;
+ inhdr = in + NBT_HDR_SIZE;
- if (smbXcli_conn_has_async_calls(cli->conn)) {
- return NT_STATUS_INVALID_PARAMETER;
- }
- ev = samba_tevent_context_init(talloc_tos());
- if (ev == NULL) {
- goto fail;
- }
- req = cli_session_setup_send(ev, ev, cli, user, pass, passlen,
- ntpass, ntpasslen, workgroup);
- if (req == NULL) {
- goto fail;
- }
- if (!tevent_req_poll_ntstatus(req, ev, &status)) {
- goto fail;
+ if (num_bytes) {
+ if (clistr_pull_talloc(cli,
+ (const char *)inhdr,
+ SVAL(inhdr, HDR_FLG2),
+ &cli->dev,
+ bytes,
+ num_bytes,
+ STR_TERMINATE|STR_ASCII) == -1) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ } else {
+ cli->dev = talloc_strdup(cli, "");
+ if (cli->dev == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
}
- status = cli_session_setup_recv(req);
- fail:
- TALLOC_FREE(ev);
- return status;
-}
-
-/****************************************************************************
- Send a uloggoff.
-*****************************************************************************/
-struct cli_ulogoff_state {
- struct cli_state *cli;
- uint16_t vwv[3];
-};
-
-static void cli_ulogoff_done(struct tevent_req *subreq);
+ if ((smbXcli_conn_protocol(cli->conn) >= PROTOCOL_NT1) && (num_bytes == 3)) {
+ /* almost certainly win95 - enable bug fixes */
+ cli->win95 = True;
+ }
-static struct tevent_req *cli_ulogoff_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct cli_state *cli)
-{
- struct tevent_req *req, *subreq;
- struct cli_ulogoff_state *state;
+ /*
+ * Make sure that we have the optional support 16-bit field. WCT > 2.
+ * Avoids issues when connecting to Win9x boxes sharing files
+ */
- req = tevent_req_create(mem_ctx, &state, struct cli_ulogoff_state);
- if (req == NULL) {
- return NULL;
+ if ((wct > 2) && (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_LANMAN2)) {
+ optional_support = SVAL(vwv+2, 0);
}
- state->cli = cli;
-
- 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);
+ if (optional_support & SMB_EXTENDED_SIGNATURES) {
+ smb1cli_session_protect_session_key(cli->smb1.session);
}
- 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;
+ smb1cli_tcon_set_values(state->cli->smb1.tcon,
+ SVAL(inhdr, HDR_TID),
+ optional_support,
+ 0, /* maximal_access */
+ 0, /* guest_maximal_access */
+ NULL, /* service */
+ NULL); /* fs_type */
- status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
- cli_state_set_uid(state->cli, UID_FIELD_INVALID);
tevent_req_done(req);
}
-static NTSTATUS cli_ulogoff_recv(struct tevent_req *req)
+NTSTATUS cli_tcon_andx_recv(struct tevent_req *req)
{
return tevent_req_simple_recv_ntstatus(req);
}
-NTSTATUS cli_ulogoff(struct cli_state *cli)
+NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share,
+ const char *dev, const char *pass, int passlen)
{
+ TALLOC_CTX *frame = talloc_stackframe();
struct tevent_context *ev;
struct tevent_req *req;
NTSTATUS status = NT_STATUS_NO_MEMORY;
- if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
- status = smb2cli_logoff(cli->conn,
- cli->timeout,
- cli->smb2.session);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
- smb2cli_session_set_id_and_flags(cli->smb2.session,
- UINT64_MAX, 0);
- return NT_STATUS_OK;
- }
-
if (smbXcli_conn_has_async_calls(cli->conn)) {
- return NT_STATUS_INVALID_PARAMETER;
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
}
- ev = samba_tevent_context_init(talloc_tos());
+
+ ev = samba_tevent_context_init(frame);
if (ev == NULL) {
goto fail;
}
- req = cli_ulogoff_send(ev, ev, cli);
+
+ req = cli_tcon_andx_send(frame, ev, cli, share, dev, pass, passlen);
if (req == NULL) {
goto fail;
}
+
if (!tevent_req_poll_ntstatus(req, ev, &status)) {
goto fail;
}
- status = cli_ulogoff_recv(req);
-fail:
- TALLOC_FREE(ev);
+
+ status = cli_tcon_andx_recv(req);
+ fail:
+ TALLOC_FREE(frame);
return status;
}
-/****************************************************************************
- Send a tconX.
-****************************************************************************/
-
-struct cli_tcon_andx_state {
+struct cli_tree_connect_state {
struct cli_state *cli;
- uint16_t vwv[4];
- struct iovec bytes;
};
-static void cli_tcon_andx_done(struct tevent_req *subreq);
+static struct tevent_req *cli_raw_tcon_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ const char *service, const char *pass, const char *dev);
+static NTSTATUS cli_raw_tcon_recv(struct tevent_req *req,
+ uint16_t *max_xmit, uint16_t *tid);
-struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct cli_state *cli,
- const char *share, const char *dev,
- const char *pass, int passlen,
- struct tevent_req **psmbreq)
+static void cli_tree_connect_smb2_done(struct tevent_req *subreq);
+static void cli_tree_connect_andx_done(struct tevent_req *subreq);
+static void cli_tree_connect_raw_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_tree_connect_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ const char *share, const char *dev, const char *pass)
{
struct tevent_req *req, *subreq;
- struct cli_tcon_andx_state *state;
- uint8_t p24[24];
- uint16_t *vwv;
- char *tmp = NULL;
- uint8_t *bytes;
- uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
- uint16_t tcon_flags = 0;
+ struct cli_tree_connect_state *state;
+ int passlen;
- *psmbreq = NULL;
+ if (pass == NULL) {
+ pass = "";
+ }
+ passlen = strlen(pass) + 1;
- req = tevent_req_create(mem_ctx, &state, struct cli_tcon_andx_state);
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_tree_connect_state);
if (req == NULL) {
return NULL;
}
state->cli = cli;
- vwv = state->vwv;
cli->share = talloc_strdup(cli, share);
- if (!cli->share) {
- return NULL;
+ if (tevent_req_nomem(cli->share, req)) {
+ return tevent_req_post(req, ev);
}
- /* in user level security don't send a password now */
- if (sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) {
- passlen = 1;
- pass = "";
- } else if (pass == NULL) {
- DEBUG(1, ("Server not using user level security and no "
- "password supplied.\n"));
- goto access_denied;
- }
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ char *unc;
- if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) &&
- *pass && passlen != 24) {
- if (!lp_client_lanman_auth()) {
- DEBUG(1, ("Server requested LANMAN password "
- "(share-level security) but "
- "'client lanman auth = no' or 'client ntlmv2 auth = yes'\n"));
- goto access_denied;
+ TALLOC_FREE(cli->smb2.tcon);
+ cli->smb2.tcon = smbXcli_tcon_create(cli);
+ if (tevent_req_nomem(cli->smb2.tcon, req)) {
+ return tevent_req_post(req, ev);
}
- /*
- * Non-encrypted passwords - convert to DOS codepage before
- * encryption.
- */
- SMBencrypt(pass, smb1cli_conn_server_challenge(cli->conn), p24);
- passlen = 24;
- pass = (const char *)p24;
- } else {
- if((sec_mode & (NEGOTIATE_SECURITY_USER_LEVEL
- |NEGOTIATE_SECURITY_CHALLENGE_RESPONSE))
- == 0) {
- uint8_t *tmp_pass;
+ unc = talloc_asprintf(state, "\\\\%s\\%s",
+ smbXcli_conn_remote_name(cli->conn),
+ share);
+ if (tevent_req_nomem(unc, req)) {
+ return tevent_req_post(req, ev);
+ }
- if (!lp_client_plaintext_auth() && (*pass)) {
- DEBUG(1, ("Server requested PLAINTEXT "
- "password but "
- "'client plaintext auth = no' or 'client ntlmv2 auth = yes'\n"));
- goto access_denied;
- }
+ subreq = smb2cli_tcon_send(state, ev, cli->conn, cli->timeout,
+ cli->smb2.session, cli->smb2.tcon,
+ 0, /* flags */
+ unc);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_tree_connect_smb2_done,
+ req);
+ return req;
+ }
- /*
- * Non-encrypted passwords - convert to DOS codepage
- * before using.
- */
- tmp_pass = talloc_array(talloc_tos(), uint8_t, 0);
- if (tevent_req_nomem(tmp_pass, req)) {
- return tevent_req_post(req, ev);
- }
- tmp_pass = trans2_bytes_push_str(tmp_pass,
- false, /* always DOS */
- pass,
- passlen,
- NULL);
- if (tevent_req_nomem(tmp_pass, req)) {
- return tevent_req_post(req, ev);
- }
- pass = (const char *)tmp_pass;
- passlen = talloc_get_size(tmp_pass);
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_LANMAN1) {
+ subreq = cli_tcon_andx_send(state, ev, cli, share, dev,
+ pass, passlen);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
}
+ tevent_req_set_callback(subreq, cli_tree_connect_andx_done,
+ req);
+ return req;
}
- tcon_flags |= TCONX_FLAG_EXTENDED_RESPONSE;
- tcon_flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
+ subreq = cli_raw_tcon_send(state, ev, cli, share, pass, dev);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_tree_connect_raw_done, req);
- SCVAL(vwv+0, 0, 0xFF);
- SCVAL(vwv+0, 1, 0);
- SSVAL(vwv+1, 0, 0);
- SSVAL(vwv+2, 0, tcon_flags);
- SSVAL(vwv+3, 0, passlen);
+ return req;
+}
- if (passlen && pass) {
- bytes = (uint8_t *)talloc_memdup(state, pass, passlen);
- } else {
- bytes = talloc_array(state, uint8_t, 0);
- }
+static void cli_tree_connect_smb2_done(struct tevent_req *subreq)
+{
+ tevent_req_simple_finish_ntstatus(
+ subreq, smb2cli_tcon_recv(subreq));
+}
- /*
- * Add the sharename
- */
- tmp = talloc_asprintf_strupper_m(talloc_tos(), "\\\\%s\\%s",
- smbXcli_conn_remote_name(cli->conn), share);
- if (tmp == NULL) {
- TALLOC_FREE(req);
- return NULL;
- }
- bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), tmp, strlen(tmp)+1,
- NULL);
- TALLOC_FREE(tmp);
+static void cli_tree_connect_andx_done(struct tevent_req *subreq)
+{
+ tevent_req_simple_finish_ntstatus(
+ subreq, cli_tcon_andx_recv(subreq));
+}
- /*
- * Add the devicetype
- */
- tmp = talloc_strdup_upper(talloc_tos(), dev);
- if (tmp == NULL) {
- TALLOC_FREE(req);
- return NULL;
+static void cli_tree_connect_raw_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_tree_connect_state *state = tevent_req_data(
+ req, struct cli_tree_connect_state);
+ NTSTATUS status;
+ uint16_t max_xmit = 0;
+ uint16_t tid = 0;
+
+ status = cli_raw_tcon_recv(subreq, &max_xmit, &tid);
+ if (tevent_req_nterror(req, status)) {
+ return;
}
- bytes = smb_bytes_push_str(bytes, false, tmp, strlen(tmp)+1, NULL);
- TALLOC_FREE(tmp);
- if (bytes == NULL) {
- TALLOC_FREE(req);
- return NULL;
+ smb1cli_tcon_set_values(state->cli->smb1.tcon,
+ tid,
+ 0, /* optional_support */
+ 0, /* maximal_access */
+ 0, /* guest_maximal_access */
+ NULL, /* service */
+ NULL); /* fs_type */
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_tree_connect_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_tree_connect(struct cli_state *cli, const char *share,
+ const char *dev, const char *pass)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_tree_connect_send(ev, ev, cli, share, dev, pass);
+ if (req == NULL) {
+ goto fail;
}
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_tree_connect_recv(req);
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
- state->bytes.iov_base = (void *)bytes;
- state->bytes.iov_len = talloc_get_size(bytes);
+NTSTATUS cli_tree_connect_creds(struct cli_state *cli,
+ const char *share, const char *dev,
+ struct cli_credentials *creds)
+{
+ const char *pw = NULL;
- subreq = cli_smb_req_create(state, ev, cli, SMBtconX, 0, 4, vwv,
- 1, &state->bytes);
- if (subreq == NULL) {
- TALLOC_FREE(req);
- return NULL;
+ if (creds != NULL) {
+ pw = cli_credentials_get_password(creds);
}
- tevent_req_set_callback(subreq, cli_tcon_andx_done, req);
- *psmbreq = subreq;
- return req;
- access_denied:
- tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
- return tevent_req_post(req, ev);
+ return cli_tree_connect(cli, share, dev, pw);
}
-struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct cli_state *cli,
- const char *share, const char *dev,
- const char *pass, int passlen)
+/****************************************************************************
+ Send a tree disconnect.
+****************************************************************************/
+
+struct cli_tdis_state {
+ struct cli_state *cli;
+};
+
+static void cli_tdis_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_tdis_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli)
{
struct tevent_req *req, *subreq;
- NTSTATUS status;
+ struct cli_tdis_state *state;
- req = cli_tcon_andx_create(mem_ctx, ev, cli, share, dev, pass, passlen,
- &subreq);
+ req = tevent_req_create(mem_ctx, &state, struct cli_tdis_state);
if (req == NULL) {
return NULL;
}
- if (subreq == NULL) {
- return req;
- }
- status = smb1cli_req_chain_submit(&subreq, 1);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
+ state->cli = cli;
+
+ subreq = cli_smb_send(state, ev, cli, SMBtdis, 0, 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;
}
-static void cli_tcon_andx_done(struct tevent_req *subreq)
+static void cli_tdis_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
- struct cli_tcon_andx_state *state = tevent_req_data(
- req, struct cli_tcon_andx_state);
- struct cli_state *cli = state->cli;
- uint8_t *in;
- uint8_t *inhdr;
- uint8_t wct;
- uint16_t *vwv;
- uint32_t num_bytes;
- uint8_t *bytes;
+ struct cli_tdis_state *state = tevent_req_data(
+ req, struct cli_tdis_state);
NTSTATUS status;
- uint16_t optional_support = 0;
- status = cli_smb_recv(subreq, state, &in, 0, &wct, &vwv,
- &num_bytes, &bytes);
+ status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
TALLOC_FREE(subreq);
if (!NT_STATUS_IS_OK(status)) {
tevent_req_nterror(req, status);
return;
}
+ TALLOC_FREE(state->cli->smb1.tcon);
+ tevent_req_done(req);
+}
- inhdr = in + NBT_HDR_SIZE;
+static NTSTATUS cli_tdis_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
- if (num_bytes) {
- if (clistr_pull_talloc(cli,
- (const char *)inhdr,
- SVAL(inhdr, HDR_FLG2),
- &cli->dev,
- bytes,
- num_bytes,
- STR_TERMINATE|STR_ASCII) == -1) {
- tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
- return;
- }
- } else {
- cli->dev = talloc_strdup(cli, "");
- if (cli->dev == NULL) {
- tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
- return;
+NTSTATUS cli_tdis(struct cli_state *cli)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ status = smb2cli_tdis(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon);
+ if (NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(cli->smb2.tcon);
}
+ return status;
}
- if ((smbXcli_conn_protocol(cli->conn) >= PROTOCOL_NT1) && (num_bytes == 3)) {
- /* almost certainly win95 - enable bug fixes */
- cli->win95 = True;
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ return NT_STATUS_INVALID_PARAMETER;
}
-
- /*
- * Make sure that we have the optional support 16-bit field. WCT > 2.
- * Avoids issues when connecting to Win9x boxes sharing files
- */
-
- if ((wct > 2) && (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_LANMAN2)) {
- optional_support = SVAL(vwv+2, 0);
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
}
-
- if (optional_support & SMB_EXTENDED_SIGNATURES) {
- smb1cli_session_protect_session_key(cli->smb1.session);
+ 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);
+ return status;
+}
- smb1cli_tcon_set_values(state->cli->smb1.tcon,
- SVAL(inhdr, HDR_TID),
- optional_support,
- 0, /* maximal_access */
- 0, /* guest_maximal_access */
- NULL, /* service */
- NULL); /* fs_type */
+struct cli_connect_sock_state {
+ const char **called_names;
+ const char **calling_names;
+ int *called_types;
+ int fd;
+ uint16_t port;
+};
- tevent_req_done(req);
-}
+static void cli_connect_sock_done(struct tevent_req *subreq);
-NTSTATUS cli_tcon_andx_recv(struct tevent_req *req)
-{
- return tevent_req_simple_recv_ntstatus(req);
-}
+/*
+ * Async only if we don't have to look up the name, i.e. "pss" is set with a
+ * nonzero address.
+ */
-NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share,
- const char *dev, const char *pass, int passlen)
+static struct tevent_req *cli_connect_sock_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *host, int name_type, const struct sockaddr_storage *pss,
+ const char *myname, uint16_t port)
{
- TALLOC_CTX *frame = talloc_stackframe();
- struct tevent_context *ev;
- struct tevent_req *req;
- NTSTATUS status = NT_STATUS_NO_MEMORY;
+ struct tevent_req *req, *subreq;
+ struct cli_connect_sock_state *state;
+ struct sockaddr_storage *addrs;
+ unsigned i, num_addrs;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_connect_sock_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if ((pss == NULL) || is_zero_addr(pss)) {
- if (smbXcli_conn_has_async_calls(cli->conn)) {
/*
- * Can't use sync call while an async call is in flight
+ * Here we cheat. resolve_name_list is not async at all. So
+ * this call will only be really async if the name lookup has
+ * been done externally.
*/
- status = NT_STATUS_INVALID_PARAMETER;
- goto fail;
+
+ status = resolve_name_list(state, host, name_type,
+ &addrs, &num_addrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ addrs = talloc_array(state, struct sockaddr_storage, 1);
+ if (tevent_req_nomem(addrs, req)) {
+ return tevent_req_post(req, ev);
+ }
+ addrs[0] = *pss;
+ num_addrs = 1;
}
- ev = samba_tevent_context_init(frame);
- if (ev == NULL) {
- goto fail;
+ state->called_names = talloc_array(state, const char *, num_addrs);
+ if (tevent_req_nomem(state->called_names, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->called_types = talloc_array(state, int, num_addrs);
+ if (tevent_req_nomem(state->called_types, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->calling_names = talloc_array(state, const char *, num_addrs);
+ if (tevent_req_nomem(state->calling_names, req)) {
+ return tevent_req_post(req, ev);
+ }
+ for (i=0; i<num_addrs; i++) {
+ state->called_names[i] = host;
+ state->called_types[i] = name_type;
+ state->calling_names[i] = myname;
}
- req = cli_tcon_andx_send(frame, ev, cli, share, dev, pass, passlen);
- if (req == NULL) {
- goto fail;
+ subreq = smbsock_any_connect_send(
+ state, ev, addrs, state->called_names, state->called_types,
+ state->calling_names, NULL, num_addrs, port);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_connect_sock_done, req);
+ return req;
+}
+
+static void cli_connect_sock_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_connect_sock_state *state = tevent_req_data(
+ req, struct cli_connect_sock_state);
+ NTSTATUS status;
+
+ status = smbsock_any_connect_recv(subreq, &state->fd, NULL,
+ &state->port);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
}
+ set_socket_options(state->fd, lp_socket_options());
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_connect_sock_recv(struct tevent_req *req,
+ int *pfd, uint16_t *pport)
+{
+ struct cli_connect_sock_state *state = tevent_req_data(
+ req, struct cli_connect_sock_state);
+ NTSTATUS status;
- if (!tevent_req_poll_ntstatus(req, ev, &status)) {
- goto fail;
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
}
-
- status = cli_tcon_andx_recv(req);
- fail:
- TALLOC_FREE(frame);
- return status;
+ *pfd = state->fd;
+ *pport = state->port;
+ return NT_STATUS_OK;
}
-struct cli_tree_connect_state {
+struct cli_connect_nb_state {
+ const char *desthost;
+ int signing_state;
+ int flags;
struct cli_state *cli;
};
-static struct tevent_req *cli_raw_tcon_send(
- TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
- const char *service, const char *pass, const char *dev);
-static NTSTATUS cli_raw_tcon_recv(struct tevent_req *req,
- uint16_t *max_xmit, uint16_t *tid);
-
-static void cli_tree_connect_smb2_done(struct tevent_req *subreq);
-static void cli_tree_connect_andx_done(struct tevent_req *subreq);
-static void cli_tree_connect_raw_done(struct tevent_req *subreq);
+static void cli_connect_nb_done(struct tevent_req *subreq);
-static struct tevent_req *cli_tree_connect_send(
- TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
- const char *share, const char *dev, const char *pass, int passlen)
+static struct tevent_req *cli_connect_nb_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *host, const struct sockaddr_storage *dest_ss,
+ uint16_t port, int name_type, const char *myname,
+ int signing_state, int flags)
{
struct tevent_req *req, *subreq;
- struct cli_tree_connect_state *state;
+ struct cli_connect_nb_state *state;
- req = tevent_req_create(mem_ctx, &state,
- struct cli_tree_connect_state);
+ req = tevent_req_create(mem_ctx, &state, struct cli_connect_nb_state);
if (req == NULL) {
return NULL;
}
- state->cli = cli;
-
- cli->share = talloc_strdup(cli, share);
- if (tevent_req_nomem(cli->share, req)) {
- return tevent_req_post(req, ev);
- }
-
- if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
- char *unc;
-
- cli->smb2.tcon = smbXcli_tcon_create(cli);
- if (tevent_req_nomem(cli->smb2.tcon, req)) {
- return tevent_req_post(req, ev);
- }
+ state->signing_state = signing_state;
+ state->flags = flags;
- unc = talloc_asprintf(state, "\\\\%s\\%s",
- smbXcli_conn_remote_name(cli->conn),
- share);
- if (tevent_req_nomem(unc, req)) {
- return tevent_req_post(req, ev);
- }
+ if (host != NULL) {
+ char *p = strchr(host, '#');
- subreq = smb2cli_tcon_send(state, ev, cli->conn, cli->timeout,
- cli->smb2.session, cli->smb2.tcon,
- 0, /* flags */
- unc);
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
+ if (p != NULL) {
+ name_type = strtol(p+1, NULL, 16);
+ host = talloc_strndup(state, host, p - host);
+ if (tevent_req_nomem(host, req)) {
+ return tevent_req_post(req, ev);
+ }
}
- tevent_req_set_callback(subreq, cli_tree_connect_smb2_done,
- req);
- return req;
- }
- if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_LANMAN1) {
- subreq = cli_tcon_andx_send(state, ev, cli, share, dev,
- pass, passlen);
- if (tevent_req_nomem(subreq, req)) {
+ state->desthost = host;
+ } else if (dest_ss != NULL) {
+ state->desthost = print_canonical_sockaddr(state, dest_ss);
+ if (tevent_req_nomem(state->desthost, req)) {
return tevent_req_post(req, ev);
}
- tevent_req_set_callback(subreq, cli_tree_connect_andx_done,
- req);
- return req;
+ } else {
+ /* No host or dest_ss given. Error out. */
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
}
- subreq = cli_raw_tcon_send(state, ev, cli, share, pass, dev);
+ subreq = cli_connect_sock_send(state, ev, host, name_type, dest_ss,
+ myname, port);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
- tevent_req_set_callback(subreq, cli_tree_connect_raw_done, req);
-
+ tevent_req_set_callback(subreq, cli_connect_nb_done, req);
return req;
}
-static void cli_tree_connect_smb2_done(struct tevent_req *subreq)
-{
- tevent_req_simple_finish_ntstatus(
- subreq, smb2cli_tcon_recv(subreq));
-}
-
-static void cli_tree_connect_andx_done(struct tevent_req *subreq)
-{
- tevent_req_simple_finish_ntstatus(
- subreq, cli_tcon_andx_recv(subreq));
-}
-
-static void cli_tree_connect_raw_done(struct tevent_req *subreq)
+static void cli_connect_nb_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
- struct cli_tree_connect_state *state = tevent_req_data(
- req, struct cli_tree_connect_state);
+ struct cli_connect_nb_state *state = tevent_req_data(
+ req, struct cli_connect_nb_state);
NTSTATUS status;
- uint16_t max_xmit = 0;
- uint16_t tid = 0;
+ int fd = 0;
+ uint16_t port;
- status = cli_raw_tcon_recv(subreq, &max_xmit, &tid);
+ status = cli_connect_sock_recv(subreq, &fd, &port);
+ TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
}
- smb1cli_tcon_set_values(state->cli->smb1.tcon,
- tid,
- 0, /* optional_support */
- 0, /* maximal_access */
- 0, /* guest_maximal_access */
- NULL, /* service */
- NULL); /* fs_type */
-
+ state->cli = cli_state_create(state, fd, state->desthost,
+ state->signing_state, state->flags);
+ if (tevent_req_nomem(state->cli, req)) {
+ close(fd);
+ return;
+ }
tevent_req_done(req);
}
-static NTSTATUS cli_tree_connect_recv(struct tevent_req *req)
+static NTSTATUS cli_connect_nb_recv(struct tevent_req *req,
+ struct cli_state **pcli)
{
- return tevent_req_simple_recv_ntstatus(req);
+ struct cli_connect_nb_state *state = tevent_req_data(
+ req, struct cli_connect_nb_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pcli = talloc_move(NULL, &state->cli);
+ return NT_STATUS_OK;
}
-NTSTATUS cli_tree_connect(struct cli_state *cli, const char *share,
- const char *dev, const char *pass, int passlen)
+NTSTATUS cli_connect_nb(const char *host, const struct sockaddr_storage *dest_ss,
+ uint16_t port, int name_type, const char *myname,
+ int signing_state, int flags, struct cli_state **pcli)
{
struct tevent_context *ev;
struct tevent_req *req;
NTSTATUS status = NT_STATUS_NO_MEMORY;
- if (smbXcli_conn_has_async_calls(cli->conn)) {
- return NT_STATUS_INVALID_PARAMETER;
- }
ev = samba_tevent_context_init(talloc_tos());
if (ev == NULL) {
goto fail;
}
- req = cli_tree_connect_send(ev, ev, cli, share, dev, pass, passlen);
+ req = cli_connect_nb_send(ev, ev, host, dest_ss, port, name_type,
+ myname, signing_state, flags);
if (req == NULL) {
goto fail;
}
+ if (!tevent_req_set_endtime(req, ev, timeval_current_ofs(20, 0))) {
+ goto fail;
+ }
if (!tevent_req_poll_ntstatus(req, ev, &status)) {
goto fail;
}
- status = cli_tree_connect_recv(req);
+ status = cli_connect_nb_recv(req, pcli);
fail:
TALLOC_FREE(ev);
return status;
}
-/****************************************************************************
- Send a tree disconnect.
-****************************************************************************/
-
-struct cli_tdis_state {
+struct cli_start_connection_state {
+ struct tevent_context *ev;
struct cli_state *cli;
+ int min_protocol;
+ int max_protocol;
};
-static void cli_tdis_done(struct tevent_req *subreq);
+static void cli_start_connection_connected(struct tevent_req *subreq);
+static void cli_start_connection_done(struct tevent_req *subreq);
-static struct tevent_req *cli_tdis_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct cli_state *cli)
+/**
+ establishes a connection to after the negprot.
+ @param output_cli A fully initialised cli structure, non-null only on success
+ @param dest_host The netbios name of the remote host
+ @param dest_ss (optional) The the destination IP, NULL for name based lookup
+ @param port (optional) The destination port (0 for default)
+*/
+
+static struct tevent_req *cli_start_connection_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *my_name, const char *dest_host,
+ const struct sockaddr_storage *dest_ss, int port,
+ int signing_state, int flags)
{
struct tevent_req *req, *subreq;
- struct cli_tdis_state *state;
+ struct cli_start_connection_state *state;
- req = tevent_req_create(mem_ctx, &state, struct cli_tdis_state);
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_start_connection_state);
if (req == NULL) {
return NULL;
}
- state->cli = cli;
+ state->ev = ev;
+
+ if (signing_state == SMB_SIGNING_IPC_DEFAULT) {
+ state->min_protocol = lp_client_ipc_min_protocol();
+ state->max_protocol = lp_client_ipc_max_protocol();
+ } else {
+ state->min_protocol = lp_client_min_protocol();
+ state->max_protocol = lp_client_max_protocol();
+ }
+
+ if (flags & CLI_FULL_CONNECTION_FORCE_SMB1) {
+ state->max_protocol = MIN(state->max_protocol, PROTOCOL_NT1);
+ }
+
+ if (flags & CLI_FULL_CONNECTION_DISABLE_SMB1) {
+ state->min_protocol = MAX(state->max_protocol, PROTOCOL_SMB2_02);
+ state->max_protocol = MAX(state->max_protocol, PROTOCOL_LATEST);
+ }
- subreq = cli_smb_send(state, ev, cli, SMBtdis, 0, 0, NULL, 0, NULL);
+ subreq = cli_connect_nb_send(state, ev, dest_host, dest_ss, port,
+ 0x20, my_name, signing_state, flags);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
- tevent_req_set_callback(subreq, cli_tdis_done, req);
+ tevent_req_set_callback(subreq, cli_start_connection_connected, req);
return req;
}
-static void cli_tdis_done(struct tevent_req *subreq)
+static void cli_start_connection_connected(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);
+ struct cli_start_connection_state *state = tevent_req_data(
+ req, struct cli_start_connection_state);
NTSTATUS status;
- status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ status = cli_connect_nb_recv(subreq, &state->cli);
TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = smbXcli_negprot_send(state, state->ev, state->cli->conn,
+ state->cli->timeout,
+ state->min_protocol,
+ state->max_protocol,
+ WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_start_connection_done, req);
+}
+
+static void cli_start_connection_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_start_connection_state *state = tevent_req_data(
+ req, struct cli_start_connection_state);
+ NTSTATUS status;
+
+ status = smbXcli_negprot_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
return;
}
- cli_state_set_tid(state->cli, UINT16_MAX);
- tevent_req_done(req);
-}
-static NTSTATUS cli_tdis_recv(struct tevent_req *req)
-{
- return tevent_req_simple_recv_ntstatus(req);
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ /* Ensure we ask for some initial credits. */
+ smb2cli_conn_set_max_credits(state->cli->conn,
+ DEFAULT_SMB2_MAX_CREDITS);
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_start_connection_recv(struct tevent_req *req,
+ struct cli_state **output_cli)
+{
+ struct cli_start_connection_state *state = tevent_req_data(
+ req, struct cli_start_connection_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *output_cli = state->cli;
+
+ return NT_STATUS_OK;
}
-NTSTATUS cli_tdis(struct cli_state *cli)
+NTSTATUS cli_start_connection(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ const struct sockaddr_storage *dest_ss, int port,
+ int signing_state, int flags)
{
struct tevent_context *ev;
struct tevent_req *req;
NTSTATUS status = NT_STATUS_NO_MEMORY;
- if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
- return smb2cli_tdis(cli->conn,
- cli->timeout,
- cli->smb2.session,
- cli->smb2.tcon);
- }
-
- if (smbXcli_conn_has_async_calls(cli->conn)) {
- return NT_STATUS_INVALID_PARAMETER;
- }
ev = samba_tevent_context_init(talloc_tos());
if (ev == NULL) {
goto fail;
}
- req = cli_tdis_send(ev, ev, cli);
+ req = cli_start_connection_send(ev, ev, my_name, dest_host, dest_ss,
+ port, signing_state, flags);
if (req == NULL) {
goto fail;
}
if (!tevent_req_poll_ntstatus(req, ev, &status)) {
goto fail;
}
- status = cli_tdis_recv(req);
+ status = cli_start_connection_recv(req, output_cli);
fail:
TALLOC_FREE(ev);
return status;
}
-struct cli_connect_sock_state {
- const char **called_names;
- const char **calling_names;
- int *called_types;
- int fd;
- uint16_t port;
+struct cli_smb1_setup_encryption_blob_state {
+ uint16_t setup[1];
+ uint8_t param[4];
+ NTSTATUS status;
+ DATA_BLOB out;
+ uint16_t enc_ctx_id;
};
-static void cli_connect_sock_done(struct tevent_req *subreq);
-
-/*
- * Async only if we don't have to look up the name, i.e. "pss" is set with a
- * nonzero address.
- */
+static void cli_smb1_setup_encryption_blob_done(struct tevent_req *subreq);
-static struct tevent_req *cli_connect_sock_send(
- TALLOC_CTX *mem_ctx, struct tevent_context *ev,
- const char *host, int name_type, const struct sockaddr_storage *pss,
- const char *myname, uint16_t port)
+static struct tevent_req *cli_smb1_setup_encryption_blob_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const DATA_BLOB in)
{
- struct tevent_req *req, *subreq;
- struct cli_connect_sock_state *state;
- const char *prog;
- struct sockaddr_storage *addrs;
- unsigned i, num_addrs;
- NTSTATUS status;
+ struct tevent_req *req = NULL;
+ struct cli_smb1_setup_encryption_blob_state *state = NULL;
+ struct tevent_req *subreq = NULL;
req = tevent_req_create(mem_ctx, &state,
- struct cli_connect_sock_state);
+ struct cli_smb1_setup_encryption_blob_state);
if (req == NULL) {
return NULL;
}
- prog = getenv("LIBSMB_PROG");
- if (prog != NULL) {
- state->fd = sock_exec(prog);
- if (state->fd == -1) {
- status = map_nt_error_from_unix(errno);
- tevent_req_nterror(req, status);
- } else {
- state->port = 0;
- tevent_req_done(req);
- }
+ if (in.length > CLI_BUFFER_SIZE) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
return tevent_req_post(req, ev);
}
- if ((pss == NULL) || is_zero_addr(pss)) {
-
- /*
- * Here we cheat. resolve_name_list is not async at all. So
- * this call will only be really async if the name lookup has
- * been done externally.
- */
-
- status = resolve_name_list(state, host, name_type,
- &addrs, &num_addrs);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return tevent_req_post(req, ev);
- }
- } else {
- addrs = talloc_array(state, struct sockaddr_storage, 1);
- if (tevent_req_nomem(addrs, req)) {
- return tevent_req_post(req, ev);
- }
- addrs[0] = *pss;
- num_addrs = 1;
- }
-
- state->called_names = talloc_array(state, const char *, num_addrs);
- if (tevent_req_nomem(state->called_names, req)) {
- return tevent_req_post(req, ev);
- }
- state->called_types = talloc_array(state, int, num_addrs);
- if (tevent_req_nomem(state->called_types, req)) {
- return tevent_req_post(req, ev);
- }
- state->calling_names = talloc_array(state, const char *, num_addrs);
- if (tevent_req_nomem(state->calling_names, req)) {
- return tevent_req_post(req, ev);
- }
- for (i=0; i<num_addrs; i++) {
- state->called_names[i] = host;
- state->called_types[i] = name_type;
- state->calling_names[i] = myname;
- }
+ SSVAL(state->setup+0, 0, TRANSACT2_SETFSINFO);
+ SSVAL(state->param, 0, 0);
+ SSVAL(state->param, 2, SMB_REQUEST_TRANSPORT_ENCRYPTION);
- subreq = smbsock_any_connect_send(
- state, ev, addrs, state->called_names, state->called_types,
- state->calling_names, NULL, num_addrs, port);
+ subreq = smb1cli_trans_send(state, ev, cli->conn,
+ SMBtrans2,
+ 0, 0, /* _flags */
+ 0, 0, /* _flags2 */
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ NULL, /* pipe_name */
+ 0, /* fid */
+ 0, /* function */
+ 0, /* flags */
+ state->setup, 1, 0,
+ state->param, 4, 2,
+ in.data, in.length, CLI_BUFFER_SIZE);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
- tevent_req_set_callback(subreq, cli_connect_sock_done, req);
+ tevent_req_set_callback(subreq,
+ cli_smb1_setup_encryption_blob_done,
+ req);
+
return req;
}
-static void cli_connect_sock_done(struct tevent_req *subreq)
+static void cli_smb1_setup_encryption_blob_done(struct tevent_req *subreq)
{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct cli_connect_sock_state *state = tevent_req_data(
- req, struct cli_connect_sock_state);
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cli_smb1_setup_encryption_blob_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_blob_state);
+ uint8_t *rparam=NULL, *rdata=NULL;
+ uint32_t num_rparam, num_rdata;
NTSTATUS status;
- status = smbsock_any_connect_recv(subreq, &state->fd, NULL,
- &state->port);
+ status = smb1cli_trans_recv(subreq, state,
+ NULL, /* recv_flags */
+ NULL, 0, NULL, /* rsetup */
+ &rparam, 0, &num_rparam,
+ &rdata, 0, &num_rdata);
TALLOC_FREE(subreq);
+ state->status = status;
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ status = NT_STATUS_OK;
+ }
if (tevent_req_nterror(req, status)) {
return;
}
- set_socket_options(state->fd, lp_socket_options());
+
+ if (num_rparam == 2) {
+ state->enc_ctx_id = SVAL(rparam, 0);
+ }
+ TALLOC_FREE(rparam);
+
+ state->out = data_blob_const(rdata, num_rdata);
+
tevent_req_done(req);
}
-static NTSTATUS cli_connect_sock_recv(struct tevent_req *req,
- int *pfd, uint16_t *pport)
+static NTSTATUS cli_smb1_setup_encryption_blob_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *out,
+ uint16_t *enc_ctx_id)
{
- struct cli_connect_sock_state *state = tevent_req_data(
- req, struct cli_connect_sock_state);
+ struct cli_smb1_setup_encryption_blob_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_blob_state);
NTSTATUS status;
if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
return status;
}
- *pfd = state->fd;
- *pport = state->port;
- return NT_STATUS_OK;
+
+ status = state->status;
+
+ *out = state->out;
+ talloc_steal(mem_ctx, out->data);
+
+ *enc_ctx_id = state->enc_ctx_id;
+
+ tevent_req_received(req);
+ return status;
}
-struct cli_connect_nb_state {
- const char *desthost;
- int signing_state;
- int flags;
+struct cli_smb1_setup_encryption_state {
+ struct tevent_context *ev;
struct cli_state *cli;
+ struct smb_trans_enc_state *es;
+ DATA_BLOB blob_in;
+ DATA_BLOB blob_out;
+ bool local_ready;
+ bool remote_ready;
};
-static void cli_connect_nb_done(struct tevent_req *subreq);
+static void cli_smb1_setup_encryption_local_next(struct tevent_req *req);
+static void cli_smb1_setup_encryption_local_done(struct tevent_req *subreq);
+static void cli_smb1_setup_encryption_remote_next(struct tevent_req *req);
+static void cli_smb1_setup_encryption_remote_done(struct tevent_req *subreq);
+static void cli_smb1_setup_encryption_ready(struct tevent_req *req);
-static struct tevent_req *cli_connect_nb_send(
- TALLOC_CTX *mem_ctx, struct tevent_context *ev,
- const char *host, const struct sockaddr_storage *dest_ss,
- uint16_t port, int name_type, const char *myname,
- int signing_state, int flags)
-{
- struct tevent_req *req, *subreq;
- struct cli_connect_nb_state *state;
+static struct tevent_req *cli_smb1_setup_encryption_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ struct cli_credentials *creds)
+{
+ struct tevent_req *req = NULL;
+ struct cli_smb1_setup_encryption_state *state = NULL;
+ struct auth_generic_state *ags = NULL;
+ const DATA_BLOB *b = NULL;
+ bool auth_requested = false;
+ const char *target_service = NULL;
+ const char *target_hostname = NULL;
+ NTSTATUS status;
- req = tevent_req_create(mem_ctx, &state, struct cli_connect_nb_state);
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb1_setup_encryption_state);
if (req == NULL) {
return NULL;
}
- state->signing_state = signing_state;
- state->flags = flags;
+ state->ev = ev;
+ state->cli = cli;
- if (host != NULL) {
- char *p = strchr(host, '#');
+ auth_requested = cli_credentials_authentication_requested(creds);
+ if (!auth_requested) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return tevent_req_post(req, ev);
+ }
- if (p != NULL) {
- name_type = strtol(p+1, NULL, 16);
- host = talloc_strndup(state, host, p - host);
- if (tevent_req_nomem(host, req)) {
- return tevent_req_post(req, ev);
- }
- }
+ target_service = "cifs";
+ target_hostname = smbXcli_conn_remote_name(cli->conn);
- state->desthost = host;
- } else {
- state->desthost = print_canonical_sockaddr(state, dest_ss);
- if (tevent_req_nomem(state->desthost, req)) {
+ status = cli_session_creds_prepare_krb5(cli, creds);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->es = talloc_zero(state, struct smb_trans_enc_state);
+ if (tevent_req_nomem(state->es, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = auth_generic_client_prepare(state->es, &ags);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ gensec_want_feature(ags->gensec_security,
+ GENSEC_FEATURE_SIGN);
+ gensec_want_feature(ags->gensec_security,
+ GENSEC_FEATURE_SEAL);
+
+ status = auth_generic_set_creds(ags, creds);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (target_service != NULL) {
+ status = gensec_set_target_service(ags->gensec_security,
+ target_service);
+ if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
}
- subreq = cli_connect_sock_send(state, ev, host, name_type, dest_ss,
- myname, port);
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
+ if (target_hostname != NULL) {
+ status = gensec_set_target_hostname(ags->gensec_security,
+ target_hostname);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
}
- tevent_req_set_callback(subreq, cli_connect_nb_done, req);
- return req;
-}
-static void cli_connect_nb_done(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct cli_connect_nb_state *state = tevent_req_data(
- req, struct cli_connect_nb_state);
- NTSTATUS status;
- int fd = 0;
- uint16_t port;
+ gensec_set_max_update_size(ags->gensec_security,
+ CLI_BUFFER_SIZE);
- status = cli_connect_sock_recv(subreq, &fd, &port);
- TALLOC_FREE(subreq);
+ b = smbXcli_conn_server_gss_blob(state->cli->conn);
+ if (b != NULL) {
+ state->blob_in = *b;
+ }
+
+ status = auth_generic_client_start(ags, GENSEC_OID_SPNEGO);
if (tevent_req_nterror(req, status)) {
- return;
+ return tevent_req_post(req, ev);
}
- state->cli = cli_state_create(state, fd, state->desthost, NULL,
- state->signing_state, state->flags);
- if (tevent_req_nomem(state->cli, req)) {
- close(fd);
- return;
+ /*
+ * We only need the gensec_security part from here.
+ */
+ state->es->gensec_security = talloc_move(state->es,
+ &ags->gensec_security);
+ TALLOC_FREE(ags);
+
+ cli_smb1_setup_encryption_local_next(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
}
- tevent_req_done(req);
+
+ return req;
}
-static NTSTATUS cli_connect_nb_recv(struct tevent_req *req,
- struct cli_state **pcli)
+static void cli_smb1_setup_encryption_local_next(struct tevent_req *req)
{
- struct cli_connect_nb_state *state = tevent_req_data(
- req, struct cli_connect_nb_state);
- NTSTATUS status;
+ struct cli_smb1_setup_encryption_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_state);
+ struct tevent_req *subreq = NULL;
- if (tevent_req_is_nterror(req, &status)) {
- return status;
+ if (state->local_ready) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
}
- *pcli = talloc_move(NULL, &state->cli);
- return NT_STATUS_OK;
+
+ subreq = gensec_update_send(state, state->ev,
+ state->es->gensec_security,
+ state->blob_in);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb1_setup_encryption_local_done, req);
}
-NTSTATUS cli_connect_nb(const char *host, const struct sockaddr_storage *dest_ss,
- uint16_t port, int name_type, const char *myname,
- int signing_state, int flags, struct cli_state **pcli)
+static void cli_smb1_setup_encryption_local_done(struct tevent_req *subreq)
{
- struct tevent_context *ev;
- struct tevent_req *req;
- NTSTATUS status = NT_STATUS_NO_MEMORY;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cli_smb1_setup_encryption_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_state);
+ NTSTATUS status;
- ev = samba_tevent_context_init(talloc_tos());
- if (ev == NULL) {
- goto fail;
- }
- req = cli_connect_nb_send(ev, ev, host, dest_ss, port, name_type,
- myname, signing_state, flags);
- if (req == NULL) {
- goto fail;
- }
- if (!tevent_req_set_endtime(req, ev, timeval_current_ofs(20, 0))) {
- goto fail;
+ status = gensec_update_recv(subreq, state, &state->blob_out);
+ TALLOC_FREE(subreq);
+ state->blob_in = data_blob_null;
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+ {
+ tevent_req_nterror(req, status);
+ return;
}
- if (!tevent_req_poll_ntstatus(req, ev, &status)) {
- goto fail;
+
+ if (NT_STATUS_IS_OK(status)) {
+ state->local_ready = true;
}
- status = cli_connect_nb_recv(req, pcli);
-fail:
- TALLOC_FREE(ev);
- return status;
-}
-struct cli_start_connection_state {
- struct tevent_context *ev;
- struct cli_state *cli;
-};
+ /*
+ * We always get NT_STATUS_OK from the server even if it is not ready.
+ * So guess the server is ready when we are ready and already sent
+ * our last blob to the server.
+ */
+ if (state->local_ready && state->blob_out.length == 0) {
+ state->remote_ready = true;
+ }
-static void cli_start_connection_connected(struct tevent_req *subreq);
-static void cli_start_connection_done(struct tevent_req *subreq);
+ if (state->local_ready && state->remote_ready) {
+ cli_smb1_setup_encryption_ready(req);
+ return;
+ }
-/**
- establishes a connection to after the negprot.
- @param output_cli A fully initialised cli structure, non-null only on success
- @param dest_host The netbios name of the remote host
- @param dest_ss (optional) The the destination IP, NULL for name based lookup
- @param port (optional) The destination port (0 for default)
-*/
+ cli_smb1_setup_encryption_remote_next(req);
+}
-static struct tevent_req *cli_start_connection_send(
- TALLOC_CTX *mem_ctx, struct tevent_context *ev,
- const char *my_name, const char *dest_host,
- const struct sockaddr_storage *dest_ss, int port,
- int signing_state, int flags)
+static void cli_smb1_setup_encryption_remote_next(struct tevent_req *req)
{
- struct tevent_req *req, *subreq;
- struct cli_start_connection_state *state;
+ struct cli_smb1_setup_encryption_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_state);
+ struct tevent_req *subreq = NULL;
- req = tevent_req_create(mem_ctx, &state,
- struct cli_start_connection_state);
- if (req == NULL) {
- return NULL;
+ if (state->remote_ready) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
}
- state->ev = ev;
- subreq = cli_connect_nb_send(state, ev, dest_host, dest_ss, port,
- 0x20, my_name, signing_state, flags);
+ subreq = cli_smb1_setup_encryption_blob_send(state, state->ev,
+ state->cli, state->blob_out);
if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
+ return;
}
- tevent_req_set_callback(subreq, cli_start_connection_connected, req);
- return req;
+ tevent_req_set_callback(subreq,
+ cli_smb1_setup_encryption_remote_done,
+ req);
}
-static void cli_start_connection_connected(struct tevent_req *subreq)
+static void cli_smb1_setup_encryption_remote_done(struct tevent_req *subreq)
{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct cli_start_connection_state *state = tevent_req_data(
- req, struct cli_start_connection_state);
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cli_smb1_setup_encryption_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_state);
NTSTATUS status;
- status = cli_connect_nb_recv(subreq, &state->cli);
+ status = cli_smb1_setup_encryption_blob_recv(subreq, state,
+ &state->blob_in,
+ &state->es->enc_ctx_num);
TALLOC_FREE(subreq);
- if (tevent_req_nterror(req, status)) {
+ data_blob_free(&state->blob_out);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+ {
+ tevent_req_nterror(req, status);
return;
}
- subreq = smbXcli_negprot_send(state, state->ev, state->cli->conn,
- state->cli->timeout,
- lp_client_min_protocol(),
- lp_client_max_protocol());
- if (tevent_req_nomem(subreq, req)) {
+ /*
+ * We always get NT_STATUS_OK even if the server is not ready.
+ * So guess the server is ready when we are ready and sent
+ * our last blob to the server.
+ */
+ if (state->local_ready) {
+ state->remote_ready = true;
+ }
+
+ if (state->local_ready && state->remote_ready) {
+ cli_smb1_setup_encryption_ready(req);
return;
}
- tevent_req_set_callback(subreq, cli_start_connection_done, req);
+
+ cli_smb1_setup_encryption_local_next(req);
}
-static void cli_start_connection_done(struct tevent_req *subreq)
+static void cli_smb1_setup_encryption_ready(struct tevent_req *req)
{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct cli_start_connection_state *state = tevent_req_data(
- req, struct cli_start_connection_state);
- NTSTATUS status;
+ struct cli_smb1_setup_encryption_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_state);
+ struct smb_trans_enc_state *es = NULL;
- status = smbXcli_negprot_recv(subreq);
- TALLOC_FREE(subreq);
- if (tevent_req_nterror(req, status)) {
+ if (state->blob_in.length != 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
return;
}
- if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
- /* Ensure we ask for some initial credits. */
- smb2cli_conn_set_max_credits(state->cli->conn,
- DEFAULT_SMB2_MAX_CREDITS);
+ if (state->blob_out.length != 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
}
+ es = talloc_move(state->cli->conn, &state->es);
+ es->enc_on = true;
+ smb1cli_conn_set_encryption(state->cli->conn, es);
+ es = NULL;
+
tevent_req_done(req);
}
-static NTSTATUS cli_start_connection_recv(struct tevent_req *req,
- struct cli_state **output_cli)
+static NTSTATUS cli_smb1_setup_encryption_recv(struct tevent_req *req)
{
- struct cli_start_connection_state *state = tevent_req_data(
- req, struct cli_start_connection_state);
- NTSTATUS status;
-
- if (tevent_req_is_nterror(req, &status)) {
- return status;
- }
- *output_cli = state->cli;
-
- return NT_STATUS_OK;
+ return tevent_req_simple_recv_ntstatus(req);
}
-NTSTATUS cli_start_connection(struct cli_state **output_cli,
- const char *my_name,
- const char *dest_host,
- const struct sockaddr_storage *dest_ss, int port,
- int signing_state, int flags)
+NTSTATUS cli_smb1_setup_encryption(struct cli_state *cli,
+ struct cli_credentials *creds)
{
- struct tevent_context *ev;
- struct tevent_req *req;
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
NTSTATUS status = NT_STATUS_NO_MEMORY;
ev = samba_tevent_context_init(talloc_tos());
if (ev == NULL) {
goto fail;
}
- req = cli_start_connection_send(ev, ev, my_name, dest_host, dest_ss,
- port, signing_state, flags);
+ req = cli_smb1_setup_encryption_send(ev, ev, cli, creds);
if (req == NULL) {
goto fail;
}
if (!tevent_req_poll_ntstatus(req, ev, &status)) {
goto fail;
}
- status = cli_start_connection_recv(req, output_cli);
-fail:
+ status = cli_smb1_setup_encryption_recv(req);
+ fail:
TALLOC_FREE(ev);
return status;
}
@param port (optional) The destination port (0 for default)
@param service (optional) The share to make the connection to. Should be 'unqualified' in any way.
@param service_type The 'type' of serivice.
- @param user Username, unix string
- @param domain User's domain
- @param password User's password, unencrypted unix string.
+ @param creds The used user credentials
*/
-struct cli_full_connection_state {
+struct cli_full_connection_creds_state {
struct tevent_context *ev;
const char *service;
const char *service_type;
- const char *user;
- const char *domain;
- const char *password;
- int pw_len;
+ struct cli_credentials *creds;
int flags;
struct cli_state *cli;
};
-static int cli_full_connection_state_destructor(
- struct cli_full_connection_state *s);
-static void cli_full_connection_started(struct tevent_req *subreq);
-static void cli_full_connection_sess_set_up(struct tevent_req *subreq);
-static void cli_full_connection_done(struct tevent_req *subreq);
+static int cli_full_connection_creds_state_destructor(
+ struct cli_full_connection_creds_state *s)
+{
+ if (s->cli != NULL) {
+ cli_shutdown(s->cli);
+ s->cli = NULL;
+ }
+ return 0;
+}
+
+static void cli_full_connection_creds_conn_done(struct tevent_req *subreq);
+static void cli_full_connection_creds_sess_start(struct tevent_req *req);
+static void cli_full_connection_creds_sess_done(struct tevent_req *subreq);
+static void cli_full_connection_creds_tcon_start(struct tevent_req *req);
+static void cli_full_connection_creds_tcon_done(struct tevent_req *subreq);
-struct tevent_req *cli_full_connection_send(
+struct tevent_req *cli_full_connection_creds_send(
TALLOC_CTX *mem_ctx, struct tevent_context *ev,
const char *my_name, const char *dest_host,
const struct sockaddr_storage *dest_ss, int port,
const char *service, const char *service_type,
- const char *user, const char *domain,
- const char *password, int flags, int signing_state)
+ struct cli_credentials *creds,
+ int flags, int signing_state)
{
struct tevent_req *req, *subreq;
- struct cli_full_connection_state *state;
+ struct cli_full_connection_creds_state *state;
+ enum credentials_use_kerberos krb5_state;
+ uint32_t gensec_features = 0;
req = tevent_req_create(mem_ctx, &state,
- struct cli_full_connection_state);
+ struct cli_full_connection_creds_state);
if (req == NULL) {
return NULL;
}
- talloc_set_destructor(state, cli_full_connection_state_destructor);
+ talloc_set_destructor(state, cli_full_connection_creds_state_destructor);
+
+ flags &= ~CLI_FULL_CONNECTION_USE_KERBEROS;
+ flags &= ~CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
+ flags &= ~CLI_FULL_CONNECTION_USE_CCACHE;
+ flags &= ~CLI_FULL_CONNECTION_USE_NT_HASH;
+
+ krb5_state = cli_credentials_get_kerberos_state(creds);
+ switch (krb5_state) {
+ case CRED_MUST_USE_KERBEROS:
+ flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+ flags &= ~CLI_FULL_CONNECTION_DONT_SPNEGO;
+ break;
+ case CRED_AUTO_USE_KERBEROS:
+ flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+ flags |= CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
+ break;
+ case CRED_DONT_USE_KERBEROS:
+ break;
+ }
+
+ gensec_features = cli_credentials_get_gensec_features(creds);
+ if (gensec_features & GENSEC_FEATURE_NTLM_CCACHE) {
+ flags |= CLI_FULL_CONNECTION_USE_CCACHE;
+ }
state->ev = ev;
state->service = service;
state->service_type = service_type;
- state->user = user;
- state->domain = domain;
- state->password = password;
+ state->creds = creds;
state->flags = flags;
- state->pw_len = state->password ? strlen(state->password)+1 : 0;
- if (state->password == NULL) {
- state->password = "";
- }
-
subreq = cli_start_connection_send(
state, ev, my_name, dest_host, dest_ss, port,
signing_state, flags);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
- tevent_req_set_callback(subreq, cli_full_connection_started, req);
+ tevent_req_set_callback(subreq,
+ cli_full_connection_creds_conn_done,
+ req);
return req;
}
-static int cli_full_connection_state_destructor(
- struct cli_full_connection_state *s)
-{
- if (s->cli != NULL) {
- cli_shutdown(s->cli);
- s->cli = NULL;
- }
- return 0;
-}
-
-static void cli_full_connection_started(struct tevent_req *subreq)
+static void cli_full_connection_creds_conn_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
- struct cli_full_connection_state *state = tevent_req_data(
- req, struct cli_full_connection_state);
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
NTSTATUS status;
status = cli_start_connection_recv(subreq, &state->cli);
if (tevent_req_nterror(req, status)) {
return;
}
- subreq = cli_session_setup_send(
- state, state->ev, state->cli, state->user,
- state->password, state->pw_len, state->password, state->pw_len,
- state->domain);
+
+ cli_full_connection_creds_sess_start(req);
+}
+
+static void cli_full_connection_creds_sess_start(struct tevent_req *req)
+{
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
+ struct tevent_req *subreq = NULL;
+
+ subreq = cli_session_setup_creds_send(
+ state, state->ev, state->cli, state->creds);
if (tevent_req_nomem(subreq, req)) {
return;
}
- tevent_req_set_callback(subreq, cli_full_connection_sess_set_up, req);
+ tevent_req_set_callback(subreq,
+ cli_full_connection_creds_sess_done,
+ req);
}
-static void cli_full_connection_sess_set_up(struct tevent_req *subreq)
+static void cli_full_connection_creds_sess_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
- struct cli_full_connection_state *state = tevent_req_data(
- req, struct cli_full_connection_state);
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
NTSTATUS status;
- status = cli_session_setup_recv(subreq);
+ status = cli_session_setup_creds_recv(subreq);
TALLOC_FREE(subreq);
if (!NT_STATUS_IS_OK(status) &&
state->flags &= ~CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
- subreq = cli_session_setup_send(
- state, state->ev, state->cli, "", "", 0, "", 0,
- state->domain);
- if (tevent_req_nomem(subreq, req)) {
+ state->creds = cli_credentials_init_anon(state);
+ if (tevent_req_nomem(state->creds, req)) {
return;
}
- tevent_req_set_callback(
- subreq, cli_full_connection_sess_set_up, req);
+
+ cli_full_connection_creds_sess_start(req);
return;
}
return;
}
- if (state->service != NULL) {
- subreq = cli_tree_connect_send(
- state, state->ev, state->cli,
- state->service, state->service_type,
- state->password, state->pw_len);
- if (tevent_req_nomem(subreq, req)) {
- return;
- }
- tevent_req_set_callback(subreq, cli_full_connection_done, req);
+ cli_full_connection_creds_tcon_start(req);
+}
+
+static void cli_full_connection_creds_tcon_start(struct tevent_req *req)
+{
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
+ struct tevent_req *subreq = NULL;
+ const char *password = NULL;
+
+ if (state->service == NULL) {
+ tevent_req_done(req);
return;
}
- tevent_req_done(req);
+ password = cli_credentials_get_password(state->creds);
+
+ subreq = cli_tree_connect_send(state, state->ev,
+ state->cli,
+ state->service,
+ state->service_type,
+ password);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_full_connection_creds_tcon_done,
+ req);
}
-static void cli_full_connection_done(struct tevent_req *subreq)
+static void cli_full_connection_creds_tcon_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
tevent_req_done(req);
}
-NTSTATUS cli_full_connection_recv(struct tevent_req *req,
+NTSTATUS cli_full_connection_creds_recv(struct tevent_req *req,
struct cli_state **output_cli)
{
- struct cli_full_connection_state *state = tevent_req_data(
- req, struct cli_full_connection_state);
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
NTSTATUS status;
if (tevent_req_is_nterror(req, &status)) {
return NT_STATUS_OK;
}
-NTSTATUS cli_full_connection(struct cli_state **output_cli,
- const char *my_name,
- const char *dest_host,
- const struct sockaddr_storage *dest_ss, int port,
- const char *service, const char *service_type,
- const char *user, const char *domain,
- const char *password, int flags,
- int signing_state)
+NTSTATUS cli_full_connection_creds(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ const struct sockaddr_storage *dest_ss, int port,
+ const char *service, const char *service_type,
+ struct cli_credentials *creds,
+ int flags,
+ int signing_state)
{
struct tevent_context *ev;
struct tevent_req *req;
if (ev == NULL) {
goto fail;
}
- req = cli_full_connection_send(
+ req = cli_full_connection_creds_send(
ev, ev, my_name, dest_host, dest_ss, port, service,
- service_type, user, domain, password, flags, signing_state);
+ service_type, creds, flags, signing_state);
if (req == NULL) {
goto fail;
}
if (!tevent_req_poll_ntstatus(req, ev, &status)) {
goto fail;
}
- status = cli_full_connection_recv(req, output_cli);
+ status = cli_full_connection_creds_recv(req, output_cli);
fail:
TALLOC_FREE(ev);
return status;
}
+NTSTATUS cli_full_connection(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ const struct sockaddr_storage *dest_ss, int port,
+ const char *service, const char *service_type,
+ const char *user, const char *domain,
+ const char *password, int flags,
+ int signing_state)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+ bool use_kerberos = false;
+ bool fallback_after_kerberos = false;
+ bool use_ccache = false;
+ bool pw_nt_hash = false;
+ struct cli_credentials *creds = NULL;
+
+ if (flags & CLI_FULL_CONNECTION_USE_KERBEROS) {
+ use_kerberos = true;
+ }
+
+ if (flags & CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS) {
+ fallback_after_kerberos = true;
+ }
+
+ if (flags & CLI_FULL_CONNECTION_USE_CCACHE) {
+ use_ccache = true;
+ }
+
+ if (flags & CLI_FULL_CONNECTION_USE_NT_HASH) {
+ pw_nt_hash = true;
+ }
+
+ creds = cli_session_creds_init(frame,
+ user,
+ domain,
+ NULL, /* realm (use default) */
+ password,
+ use_kerberos,
+ fallback_after_kerberos,
+ use_ccache,
+ pw_nt_hash);
+ if (creds == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_full_connection_creds(output_cli, my_name,
+ dest_host, dest_ss, port,
+ service, service_type,
+ creds, flags, signing_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
/****************************************************************************
Send an old style tcon.
****************************************************************************/
return tevent_req_post(req, ev);
}
+ TALLOC_FREE(cli->smb1.tcon);
+ cli->smb1.tcon = smbXcli_tcon_create(cli);
+ if (tevent_req_nomem(cli->smb1.tcon, req)) {
+ return tevent_req_post(req, ev);
+ }
+ smb1cli_tcon_set_id(cli->smb1.tcon, UINT16_MAX);
+
bytes = talloc_array(state, uint8_t, 0);
bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0);
bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
return tevent_req_post(req, ev);
}
- subreq = cli_smb_send(state, ev, cli, SMBtcon, 0, 0, NULL,
+ subreq = cli_smb_send(state, ev, cli, SMBtcon, 0, 0, 0, NULL,
talloc_get_size(bytes), bytes);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
NTSTATUS nt_status;
uint32_t flags = CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
- if (user_info->use_kerberos) {
+ if (get_cmdline_auth_info_use_kerberos(user_info)) {
flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
}
+ flags |= CLI_FULL_CONNECTION_FORCE_SMB1;
+
nt_status = cli_full_connection(&cli, NULL, server, server_ss, 0, "IPC$", "IPC",
- user_info->username ? user_info->username : "",
+ get_cmdline_auth_info_username(user_info),
lp_workgroup(),
- user_info->password ? user_info->password : "",
+ get_cmdline_auth_info_password(user_info),
flags,
SMB_SIGNING_DEFAULT);