struct {
uint16_t session_id;
+ uint16_t action;
DATA_BLOB application_key;
bool protected_key;
} smb1;
conn->desire_signing = false;
conn->mandatory_signing = false;
break;
+ case SMB_SIGNING_DESIRED:
+ /* if the server desires it */
+ conn->allow_signing = true;
+ conn->desire_signing = true;
+ conn->mandatory_signing = false;
+ break;
+ case SMB_SIGNING_IPC_DEFAULT:
case SMB_SIGNING_REQUIRED:
/* always */
conn->allow_signing = true;
}
}
+static NTSTATUS smbXcli_req_cancel_write_req(struct tevent_req *req)
+{
+ struct smbXcli_req_state *state =
+ tevent_req_data(req,
+ struct smbXcli_req_state);
+ struct smbXcli_conn *conn = state->conn;
+ size_t num_pending = talloc_array_length(conn->pending);
+ ssize_t ret;
+ int err;
+ bool ok;
+
+ if (state->write_req == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * Check if it's possible to cancel the request.
+ * If the result is true it's not to late.
+ * See writev_cancel().
+ */
+ ok = tevent_req_cancel(state->write_req);
+ if (ok) {
+ TALLOC_FREE(state->write_req);
+
+ if (conn->protocol >= PROTOCOL_SMB2_02) {
+ /*
+ * SMB2 has a sane signing state.
+ */
+ return NT_STATUS_OK;
+ }
+
+ if (num_pending > 1) {
+ /*
+ * We have more pending requests following us. This
+ * means the signing state will be broken for them.
+ *
+ * As a solution we could add the requests directly to
+ * our outgoing queue and do the signing in the trigger
+ * function and then use writev_send() without passing a
+ * queue. That way we'll only sign packets we're most
+ * likely send to the wire.
+ */
+ return NT_STATUS_REQUEST_OUT_OF_SEQUENCE;
+ }
+
+ /*
+ * If we're the only request that's
+ * pending, we're able to recover the signing
+ * state.
+ */
+ smb_signing_cancel_reply(conn->smb1.signing,
+ state->smb1.one_way_seqnum);
+ return NT_STATUS_OK;
+ }
+
+ ret = writev_recv(state->write_req, &err);
+ TALLOC_FREE(state->write_req);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(err);
+ }
+
+ return NT_STATUS_OK;
+}
+
void smbXcli_req_unset_pending(struct tevent_req *req)
{
struct smbXcli_req_state *state =
struct smbXcli_conn *conn = state->conn;
size_t num_pending = talloc_array_length(conn->pending);
size_t i;
+ NTSTATUS cancel_status;
- TALLOC_FREE(state->write_req);
+ cancel_status = smbXcli_req_cancel_write_req(req);
if (state->smb1.mid != 0) {
/*
* This is a [nt]trans[2] request which waits
* for more than one reply.
*/
+ if (!NT_STATUS_IS_OK(cancel_status)) {
+ /*
+ * If the write_req cancel didn't work
+ * we can't use the connection anymore.
+ */
+ smbXcli_conn_disconnect(conn, cancel_status);
+ return;
+ }
return;
}
* conn->pending. So if nothing is pending anymore, we need to
* delete the socket read fde.
*/
+ /* TODO: smbXcli_conn_cancel_read_req */
TALLOC_FREE(conn->pending);
conn->read_smb_req = NULL;
+
+ if (!NT_STATUS_IS_OK(cancel_status)) {
+ /*
+ * If the write_req cancel didn't work
+ * we can't use the connection anymore.
+ */
+ smbXcli_conn_disconnect(conn, cancel_status);
+ return;
+ }
return;
}
* right thing nevertheless, the point of this routine is to
* remove ourselves from conn->pending.
*/
+
+ if (!NT_STATUS_IS_OK(cancel_status)) {
+ /*
+ * If the write_req cancel didn't work
+ * we can't use the connection anymore.
+ */
+ smbXcli_conn_disconnect(conn, cancel_status);
+ return;
+ }
return;
}
*/
conn->pending = talloc_realloc(NULL, conn->pending, struct tevent_req *,
num_pending - 1);
+
+ if (!NT_STATUS_IS_OK(cancel_status)) {
+ /*
+ * If the write_req cancel didn't work
+ * we can't use the connection anymore.
+ */
+ smbXcli_conn_disconnect(conn, cancel_status);
+ return;
+ }
return;
}
struct smbXcli_req_state *state =
tevent_req_data(req,
struct smbXcli_req_state);
-
- TALLOC_FREE(state->write_req);
+ struct smbXcli_conn *conn = state->conn;
+ NTSTATUS cancel_status;
switch (req_state) {
case TEVENT_REQ_RECEIVED:
/*
* Make sure we really remove it from
* the pending array on destruction.
+ *
+ * smbXcli_req_unset_pending() calls
+ * smbXcli_req_cancel_write_req() internal
*/
state->smb1.mid = 0;
smbXcli_req_unset_pending(req);
return;
default:
+ cancel_status = smbXcli_req_cancel_write_req(req);
+ if (!NT_STATUS_IS_OK(cancel_status)) {
+ /*
+ * If the write_req cancel didn't work
+ * we can't use the connection anymore.
+ */
+ smbXcli_conn_disconnect(conn, cancel_status);
+ return;
+ }
return;
}
}
state = tevent_req_data(req, struct smbXcli_req_state);
if (state->smb1.chained_requests == NULL) {
+ bool in_progress;
+
/*
* We're dead. No point waiting for trans2
* replies.
continue;
}
+ in_progress = tevent_req_is_in_progress(req);
+ if (!in_progress) {
+ /*
+ * already finished
+ */
+ continue;
+ }
+
/*
* we need to defer the callback, because we may notify
* more then one caller.
num_chained = talloc_array_length(chain);
for (i=0; i<num_chained; i++) {
+ bool in_progress;
+
req = chain[i];
state = tevent_req_data(req, struct smbXcli_req_state);
continue;
}
+ in_progress = tevent_req_is_in_progress(req);
+ if (!in_progress) {
+ /*
+ * already finished
+ */
+ continue;
+ }
+
/*
* we need to defer the callback, because we may notify
* more than one caller.
}
}
+static struct smbXcli_session* smbXcli_session_by_uid(struct smbXcli_conn *conn,
+ uint64_t uid)
+{
+ struct smbXcli_session *s = conn->sessions;
+
+ for (; s; s = s->next) {
+ if (s->smb2->session_id != uid) {
+ continue;
+ }
+ break;
+ }
+
+ return s;
+}
+
static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
uint8_t *buf,
size_t buflen,
goto inval;
}
- s = conn->sessions;
- for (; s; s = s->next) {
- if (s->smb2->session_id != uid) {
- continue;
- }
- break;
- }
-
+ s = smbXcli_session_by_uid(conn, uid);
if (s == NULL) {
DEBUG(10, ("unknown session_id %llu\n",
(unsigned long long)uid));
{
struct tevent_req *req;
struct smbXcli_req_state *state = NULL;
- struct iovec *iov;
- int i, num_iov;
+ struct iovec *iov = NULL;
+ int i, num_iov = 0;
NTSTATUS status;
bool defer = true;
struct smbXcli_session *last_session = NULL;
uint64_t uid = BVAL(inhdr, SMB2_HDR_SESSION_ID);
if (session == NULL) {
- struct smbXcli_session *s;
-
- s = state->conn->sessions;
- for (; s; s = s->next) {
- if (s->smb2->session_id != uid) {
- continue;
- }
-
- session = s;
- break;
- }
+ session = smbXcli_session_by_uid(state->conn,
+ uid);
}
if (session == NULL) {
}
}
if (signing_key) {
- int cmp;
- static const uint8_t zeros[16];
-
- cmp = memcmp(inhdr+SMB2_HDR_SIGNATURE,
- zeros,
- 16);
- if (cmp == 0) {
+ bool zero;
+ zero = all_zero(inhdr+SMB2_HDR_SIGNATURE, 16);
+ if (zero) {
state->smb2.signing_skipped = true;
signing_key = NULL;
}
}
SSVAL(p, 0, 2); /* ChiperCount */
- SSVAL(p, 2, SMB2_ENCRYPTION_AES128_GCM);
- SSVAL(p, 4, SMB2_ENCRYPTION_AES128_CCM);
+ /*
+ * For now we preferr CCM because our implementation
+ * is faster than GCM, see bug #11451.
+ */
+ SSVAL(p, 2, SMB2_ENCRYPTION_AES128_CCM);
+ SSVAL(p, 4, SMB2_ENCRYPTION_AES128_GCM);
b = data_blob_const(p, 6);
status = smb2_negotiate_context_add(state, &c,
}
talloc_set_destructor(session, smbXcli_session_destructor);
- DLIST_ADD_END(conn->sessions, session, struct smbXcli_session *);
+ DLIST_ADD_END(conn->sessions, session);
session->conn = conn;
memcpy(session->smb2_channel.preauth_sha512,
session->smb2_channel = src->smb2_channel;
session->disconnect_expired = src->disconnect_expired;
- DLIST_ADD_END(src->conn->sessions, session, struct smbXcli_session *);
+ DLIST_ADD_END(src->conn->sessions, session);
talloc_set_destructor(session, smbXcli_session_destructor);
return session;
}
+bool smbXcli_session_is_guest(struct smbXcli_session *session)
+{
+ if (session == NULL) {
+ return false;
+ }
+
+ if (session->conn == NULL) {
+ return false;
+ }
+
+ if (session->conn->mandatory_signing) {
+ return false;
+ }
+
+ if (session->conn->protocol >= PROTOCOL_SMB2_02) {
+ if (session->smb2->session_flags & SMB2_SESSION_FLAG_IS_GUEST) {
+ return true;
+ }
+ return false;
+ }
+
+ if (session->smb1.action & SMB_SETUP_GUEST) {
+ return true;
+ }
+
+ return false;
+}
+
bool smbXcli_session_is_authenticated(struct smbXcli_session *session)
{
const DATA_BLOB *application_key;
+ if (session == NULL) {
+ return false;
+ }
+
if (session->conn == NULL) {
return false;
}
session->smb1.session_id = session_id;
}
+void smb1cli_session_set_action(struct smbXcli_session *session,
+ uint16_t action)
+{
+ session->smb1.action = action;
+}
+
NTSTATUS smb1cli_session_set_session_key(struct smbXcli_session *session,
const DATA_BLOB _session_key)
{
if (conn->mandatory_signing) {
security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED;
}
+ if (session->smb2->should_sign) {
+ security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ }
return security_mode;
}
return prev_cs;
}
+uint16_t smb2cli_session_current_channel_sequence(struct smbXcli_session *session)
+{
+ return session->smb2->channel_sequence;
+}
+
void smb2cli_session_start_replay(struct smbXcli_session *session)
{
session->smb2->replay_active = true;
const struct iovec *recv_iov)
{
struct smbXcli_conn *conn = session->conn;
- uint16_t no_sign_flags;
+ uint16_t no_sign_flags = 0;
uint8_t session_key[16];
bool check_signature = true;
uint32_t hdr_flags;
return NT_STATUS_INVALID_PARAMETER_MIX;
}
- no_sign_flags = SMB2_SESSION_FLAG_IS_GUEST | SMB2_SESSION_FLAG_IS_NULL;
+ if (!conn->mandatory_signing) {
+ /*
+ * only allow guest sessions without
+ * mandatory signing.
+ *
+ * If we try an authentication with username != ""
+ * and the server let us in without verifying the
+ * password we don't have a negotiated session key
+ * for signing.
+ */
+ no_sign_flags = SMB2_SESSION_FLAG_IS_GUEST;
+ }
if (session->smb2->session_flags & no_sign_flags) {
session->smb2->should_sign = false;
}
talloc_set_destructor(session2, smbXcli_session_destructor);
- DLIST_ADD_END(conn->sessions, session2, struct smbXcli_session *);
+ DLIST_ADD_END(conn->sessions, session2);
session2->conn = conn;
memcpy(session2->smb2_channel.preauth_sha512,
NTSTATUS smb2cli_session_encryption_on(struct smbXcli_session *session)
{
+ if (!session->smb2->should_sign) {
+ /*
+ * We need required signing on the session
+ * in order to prevent man in the middle attacks.
+ */
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
if (session->smb2->should_encrypt) {
return NT_STATUS_OK;
}
return tcon->smb2.capabilities;
}
+uint32_t smb2cli_tcon_flags(struct smbXcli_tcon *tcon)
+{
+ return tcon->smb2.flags;
+}
+
void smb2cli_tcon_set_values(struct smbXcli_tcon *tcon,
struct smbXcli_session *session,
uint32_t tcon_id,