uint8_t io_priority;
+ bool force_channel_sequence;
+
uint8_t preauth_sha512[64];
} smb2;
uint64_t nonce_low;
uint16_t channel_sequence;
bool replay_active;
+ bool require_signed_response;
};
struct smbXcli_session {
struct tevent_req *write_req;
+ struct timeval endtime;
+
struct {
/* Space for the header including the wct */
uint8_t hdr[HDR_VWV];
uint64_t encryption_session_id;
bool signing_skipped;
+ bool require_signed_response;
bool notify_async;
bool got_async;
uint16_t cancel_flags;
return false;
}
+bool smbXcli_conn_signing_mandatory(struct smbXcli_conn *conn)
+{
+ return conn->mandatory_signing;
+}
+
+/*
+ * [MS-SMB] 2.2.2.3.5 - SMB1 support for passing through
+ * query/set commands to the file system
+ */
+bool smbXcli_conn_support_passthrough(struct smbXcli_conn *conn)
+{
+ if (conn->protocol >= PROTOCOL_SMB2_02) {
+ return true;
+ }
+
+ if (conn->smb1.capabilities & CAP_W2K_SMBS) {
+ return true;
+ }
+
+ return false;
+}
+
void smbXcli_conn_set_sockopt(struct smbXcli_conn *conn, const char *options)
{
set_socket_options(conn->sock_fd, options);
return &conn->smb1.server.guid;
}
+bool smbXcli_conn_get_force_channel_sequence(struct smbXcli_conn *conn)
+{
+ return conn->smb2.force_channel_sequence;
+}
+
+void smbXcli_conn_set_force_channel_sequence(struct smbXcli_conn *conn,
+ bool v)
+{
+ conn->smb2.force_channel_sequence = v;
+}
+
struct smbXcli_conn_samba_suicide_state {
struct smbXcli_conn *conn;
struct iovec iov;
/*
* Check if it's possible to cancel the request.
- * If the result is true it's not to late.
+ * If the result is true it's not too late.
* See writev_cancel().
*/
ok = tevent_req_cancel(state->write_req);
return ret;
}
-static uint8_t *smbXcli_iov_concat(TALLOC_CTX *mem_ctx,
- const struct iovec *iov,
- int count)
-{
- ssize_t buflen;
- uint8_t *buf;
-
- buflen = iov_buflen(iov, count);
- if (buflen == -1) {
- return NULL;
- }
-
- buf = talloc_array(mem_ctx, uint8_t, buflen);
- if (buf == NULL) {
- return NULL;
- }
-
- iov_buf(iov, count, buf, buflen);
-
- return buf;
-}
-
static void smb1cli_req_flags(enum protocol_types protocol,
uint32_t smb1_capabilities,
uint8_t smb_command,
state->smb1.iov_count = iov_count + 4;
if (timeout_msec > 0) {
- struct timeval endtime;
-
- endtime = timeval_current_ofs_msec(timeout_msec);
- if (!tevent_req_set_endtime(req, ev, endtime)) {
+ state->endtime = timeval_current_ofs_msec(timeout_msec);
+ if (!tevent_req_set_endtime(req, ev, state->endtime)) {
return req;
}
}
frame = talloc_stackframe();
- buf = smbXcli_iov_concat(frame, &iov[1], iov_count - 1);
+ buf = iov_concat(frame, &iov[1], iov_count - 1);
if (buf == NULL) {
return NT_STATUS_NO_MEMORY;
}
}
if (state->conn->protocol > PROTOCOL_NT1) {
+ DBG_ERR("called for dialect[%s] server[%s]\n",
+ smb_protocol_types_string(state->conn->protocol),
+ smbXcli_conn_remote_name(state->conn));
return NT_STATUS_REVISION_MISMATCH;
}
if (common_encryption_on(state->conn->smb1.trans_enc)) {
char *buf, *enc_buf;
- buf = (char *)smbXcli_iov_concat(talloc_tos(), iov, iov_count);
+ buf = (char *)iov_concat(talloc_tos(), iov, iov_count);
if (buf == NULL) {
return NT_STATUS_NO_MEMORY;
}
struct iovec **piov, int *pnum_iov)
{
struct iovec *iov;
- int num_iov;
+ size_t num_iov;
size_t buflen;
size_t taken;
size_t remaining;
wct_ofs = SVAL(cur[0].iov_base, 2);
if (wct_ofs < taken) {
- return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto inval;
}
if (wct_ofs > buflen) {
- return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto inval;
}
/*
conn->smb2.max_credits = max_credits;
}
+uint16_t smb2cli_conn_get_cur_credits(struct smbXcli_conn *conn)
+{
+ return conn->smb2.cur_credits;
+}
+
uint8_t smb2cli_conn_get_io_priority(struct smbXcli_conn *conn)
{
if (conn->protocol < PROTOCOL_SMB3_11) {
TALLOC_FREE(subreq);
}
+struct timeval smbXcli_req_endtime(struct tevent_req *req)
+{
+ struct smbXcli_req_state *state = tevent_req_data(
+ req, struct smbXcli_req_state);
+
+ return state->endtime;
+}
+
struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct smbXcli_conn *conn,
uint32_t flags = 0;
uint32_t tid = 0;
uint64_t uid = 0;
- bool use_channel_sequence = false;
+ bool use_channel_sequence = conn->smb2.force_channel_sequence;
uint16_t channel_sequence = 0;
bool use_replay_flag = false;
state->smb2.should_sign = session->smb2->should_sign;
state->smb2.should_encrypt = session->smb2->should_encrypt;
+ state->smb2.require_signed_response =
+ session->smb2->require_signed_response;
if (cmd == SMB2_OP_SESSSETUP &&
session->smb2_channel.signing_key.length == 0 &&
}
if (timeout_msec > 0) {
- struct timeval endtime;
-
- endtime = timeval_current_ofs_msec(timeout_msec);
- if (!tevent_req_set_endtime(req, ev, endtime)) {
+ state->endtime = timeval_current_ofs_msec(timeout_msec);
+ if (!tevent_req_set_endtime(req, ev, state->endtime)) {
return req;
}
}
avail = MIN(avail, state->conn->smb2.cur_credits);
if (avail < charge) {
+ DBG_ERR("Insufficient credits. "
+ "%"PRIu64" available, %"PRIu16" needed\n",
+ avail, charge);
return NT_STATUS_INTERNAL_ERROR;
}
uint8_t *buf,
size_t buflen,
TALLOC_CTX *mem_ctx,
- struct iovec **piov, int *pnum_iov)
+ struct iovec **piov,
+ size_t *pnum_iov)
{
struct iovec *iov;
int num_iov = 0;
struct tevent_req *req;
struct smbXcli_req_state *state = NULL;
struct iovec *iov = NULL;
- int i, num_iov = 0;
+ size_t i, num_iov = 0;
NTSTATUS status;
bool defer = true;
struct smbXcli_session *last_session = NULL;
}
last_session = session;
- if (state->smb2.should_sign) {
- if (!(flags & SMB2_HDR_FLAG_SIGNED)) {
- return NT_STATUS_ACCESS_DENIED;
- }
- }
-
if (flags & SMB2_HDR_FLAG_SIGNED) {
uint64_t uid = BVAL(inhdr, SMB2_HDR_SESSION_ID);
*/
signing_key = NULL;
}
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * Only check the signature of the last response
+ * of a successfull session auth. This matches
+ * Windows behaviour for NTLM auth and reauth.
+ */
+ state->smb2.require_signed_response = false;
+ }
+ }
+
+ if (state->smb2.should_sign ||
+ state->smb2.require_signed_response)
+ {
+ if (!(flags & SMB2_HDR_FLAG_SIGNED)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ if (signing_key == NULL && state->smb2.require_signed_response) {
+ signing_key = &session->smb2_channel.signing_key;
}
if (cur[0].iov_len == SMB2_TF_HDR_SIZE) {
}
if (signing_key) {
- status = smb2_signing_check_pdu(*signing_key,
- state->conn->protocol,
- &cur[1], 3);
- if (!NT_STATUS_IS_OK(status)) {
+ NTSTATUS signing_status;
+
+ signing_status = smb2_signing_check_pdu(*signing_key,
+ state->conn->protocol,
+ &cur[1], 3);
+ if (!NT_STATUS_IS_OK(signing_status)) {
/*
* If the signing check fails, we disconnect
* the connection.
*/
- return status;
+ return signing_status;
}
}
}
if (conn->protocol == PROTOCOL_NONE) {
+ DBG_ERR("No compatible protocol selected by server.\n");
tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
return;
}
return;
}
+ /*
+ * Here we are now at SMB3_11, so encryption should be
+ * negotiated via context, not capabilities.
+ */
+
if (conn->smb2.server.capabilities & SMB2_CAP_ENCRYPTION) {
- tevent_req_nterror(req,
- NT_STATUS_INVALID_NETWORK_RESPONSE);
- return;
+ /*
+ * Server set SMB2_CAP_ENCRYPTION capability,
+ * but *SHOULD* not, not *MUST* not. Just mask it off.
+ * NetApp seems to do this:
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13009
+ */
+ conn->smb2.server.capabilities &= ~SMB2_CAP_ENCRYPTION;
}
negotiate_context_offset = IVAL(body, 60);
tevent_req_done(req);
return;
}
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ /*
+ * The response was signed, but not supported
+ *
+ * This might be returned by older Windows versions or by
+ * NetApp SMB server implementations.
+ *
+ * See
+ *
+ * https://blogs.msdn.microsoft.com/openspecification/2012/06/28/smb3-secure-dialect-negotiation/
+ *
+ */
+ tevent_req_done(req);
+ return;
+ }
if (tevent_req_nterror(req, status)) {
return;
}
session->smb2->replay_active = false;
}
+void smb2cli_session_require_signed_response(struct smbXcli_session *session,
+ bool require_signed_response)
+{
+ session->smb2->require_signed_response = require_signed_response;
+}
+
NTSTATUS smb2cli_session_update_preauth(struct smbXcli_session *session,
const struct iovec *iov)
{
struct _derivation encryption;
struct _derivation decryption;
struct _derivation application;
- } derivation = { };
+ } derivation = {
+ .signing.label.length = 0,
+ };
size_t nonce_size = 0;
if (conn == NULL) {
};
struct {
struct _derivation signing;
- } derivation = { };
+ } derivation = {
+ .signing.label.length = 0,
+ };
if (conn == NULL) {
return NT_STATUS_INVALID_PARAMETER_MIX;
return tcon;
}
+/*
+ * Return a deep structure copy of a struct smbXcli_tcon *
+ */
+
+struct smbXcli_tcon *smbXcli_tcon_copy(TALLOC_CTX *mem_ctx,
+ const struct smbXcli_tcon *tcon_in)
+{
+ struct smbXcli_tcon *tcon;
+
+ tcon = talloc_memdup(mem_ctx, tcon_in, sizeof(struct smbXcli_tcon));
+ if (tcon == NULL) {
+ return NULL;
+ }
+
+ /* Deal with the SMB1 strings. */
+ if (tcon_in->smb1.service != NULL) {
+ tcon->smb1.service = talloc_strdup(tcon, tcon_in->smb1.service);
+ if (tcon->smb1.service == NULL) {
+ TALLOC_FREE(tcon);
+ return NULL;
+ }
+ }
+ if (tcon->smb1.fs_type != NULL) {
+ tcon->smb1.fs_type = talloc_strdup(tcon, tcon_in->smb1.fs_type);
+ if (tcon->smb1.fs_type == NULL) {
+ TALLOC_FREE(tcon);
+ return NULL;
+ }
+ }
+ return tcon;
+}
+
void smbXcli_tcon_set_fs_attributes(struct smbXcli_tcon *tcon,
uint32_t fs_attributes)
{
return tcon->smb2.tcon_id;
}
+void smb2cli_tcon_set_id(struct smbXcli_tcon *tcon, uint32_t tcon_id)
+{
+ tcon->smb2.tcon_id = tcon_id;
+}
+
uint32_t smb2cli_tcon_capabilities(struct smbXcli_tcon *tcon)
{
return tcon->smb2.capabilities;
{
return tcon->smb2.should_encrypt;
}
+
+void smb2cli_conn_set_mid(struct smbXcli_conn *conn, uint64_t mid)
+{
+ conn->smb2.mid = mid;
+}
+
+uint64_t smb2cli_conn_get_mid(struct smbXcli_conn *conn)
+{
+ return conn->smb2.mid;
+}