X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=libcli%2Fsmb%2FsmbXcli_base.c;h=082b62604b1f49bf1a4e5dff89ab75503000feb0;hb=b16b469f3fa3766c19ca30f377cc7de6782c05e6;hp=a363b4451c0991dba664a576cb52e85834e37672;hpb=8cb6557d22fef2570156f56a8bad00cac6b7e016;p=mat%2Fsamba.git diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index a363b4451c..082b62604b 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -148,6 +148,8 @@ struct smbXcli_session { struct { uint16_t session_id; + DATA_BLOB application_key; + bool protected_key; } smb1; struct smb2cli_session *smb2; @@ -155,9 +157,19 @@ struct smbXcli_session { struct { DATA_BLOB signing_key; } smb2_channel; + + /* + * this should be a short term hack + * until the upper layers have implemented + * re-authentication. + */ + bool disconnect_expired; }; struct smbXcli_tcon { + bool is_smb1; + uint32_t fs_attributes; + struct { uint16_t tcon_id; uint16_t optional_support; @@ -237,10 +249,16 @@ struct smbXcli_req_state { */ struct iovec *recv_iov; + /* + * the expected max for the response dyn_len + */ + uint32_t max_dyn_len; + uint16_t credit_charge; bool should_sign; bool should_encrypt; + uint64_t encryption_session_id; bool signing_skipped; bool notify_async; @@ -551,7 +569,7 @@ NTSTATUS smbXcli_conn_samba_suicide(struct smbXcli_conn *conn, status = NT_STATUS_INVALID_PARAMETER_MIX; goto fail; } - ev = tevent_context_init(frame); + ev = samba_tevent_context_init(frame); if (ev == NULL) { goto fail; } @@ -580,6 +598,23 @@ uint32_t smb1cli_conn_max_xmit(struct smbXcli_conn *conn) return conn->smb1.max_xmit; } +bool smb1cli_conn_req_possible(struct smbXcli_conn *conn) +{ + size_t pending; + uint16_t possible = conn->smb1.server.max_mux; + + pending = tevent_queue_length(conn->outgoing); + if (pending >= possible) { + return false; + } + pending += talloc_array_length(conn->pending); + if (pending >= possible) { + return false; + } + + return true; +} + uint32_t smb1cli_conn_server_session_key(struct smbXcli_conn *conn) { return conn->smb1.server.session_key; @@ -632,7 +667,10 @@ bool smb1cli_conn_activate_signing(struct smbXcli_conn *conn, bool smb1cli_conn_check_signing(struct smbXcli_conn *conn, const uint8_t *buf, uint32_t seqnum) { - return smb_signing_check_pdu(conn->smb1.signing, buf, seqnum); + const uint8_t *hdr = buf + NBT_HDR_SIZE; + size_t len = smb_len_nbt(buf); + + return smb_signing_check_pdu(conn->smb1.signing, hdr, len, seqnum); } bool smb1cli_conn_signing_is_active(struct smbXcli_conn *conn) @@ -702,6 +740,14 @@ static uint16_t smb1cli_alloc_mid(struct smbXcli_conn *conn) size_t num_pending = talloc_array_length(conn->pending); uint16_t result; + if (conn->protocol == PROTOCOL_NONE) { + /* + * This is what windows sends on the SMB1 Negprot request + * and some vendors reuse the SMB1 MID as SMB2 sequence number. + */ + return 0; + } + while (true) { size_t i; @@ -1226,6 +1272,19 @@ struct tevent_req *smb1cli_req_create(TALLOC_CTX *mem_ctx, if (tcon) { tid = tcon->smb1.tcon_id; + + if (tcon->fs_attributes & FILE_CASE_SENSITIVE_SEARCH) { + clear_flags |= FLAG_CASELESS_PATHNAMES; + } else { + /* Default setting, case insensitive. */ + additional_flags |= FLAG_CASELESS_PATHNAMES; + } + + if (smbXcli_conn_dfs_supported(conn) && + smbXcli_tcon_is_dfs_share(tcon)) + { + additional_flags2 |= FLAGS2_DFS_PATHNAMES; + } } state->smb1.recv_cmd = 0xFF; @@ -1338,15 +1397,17 @@ static NTSTATUS smb1cli_conn_signv(struct smbXcli_conn *conn, frame = talloc_stackframe(); - buf = smbXcli_iov_concat(frame, iov, iov_count); + buf = smbXcli_iov_concat(frame, &iov[1], iov_count - 1); if (buf == NULL) { return NT_STATUS_NO_MEMORY; } *seqnum = smb_signing_next_seqnum(conn->smb1.signing, one_way_seqnum); - smb_signing_sign_pdu(conn->smb1.signing, buf, *seqnum); - memcpy(iov[1].iov_base, buf+4, iov[1].iov_len); + smb_signing_sign_pdu(conn->smb1.signing, + buf, talloc_get_size(buf), + *seqnum); + memcpy(iov[1].iov_base, buf, iov[1].iov_len); TALLOC_FREE(frame); return NT_STATUS_OK; @@ -1547,8 +1608,7 @@ static void smbXcli_conn_received(struct tevent_req *subreq) if (subreq != conn->read_smb_req) { DEBUG(1, ("Internal error: cli_smb_received called with " "unexpected subreq\n")); - status = NT_STATUS_INTERNAL_ERROR; - smbXcli_conn_disconnect(conn, status); + smbXcli_conn_disconnect(conn, NT_STATUS_INTERNAL_ERROR); TALLOC_FREE(frame); return; } @@ -1572,7 +1632,9 @@ static void smbXcli_conn_received(struct tevent_req *subreq) * tevent_req_done(). */ return; - } else if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) { + } + + if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) { /* * We got an error, so notify all pending requests */ @@ -1600,13 +1662,27 @@ static NTSTATUS smb1cli_inbuf_parse_chain(uint8_t *buf, TALLOC_CTX *mem_ctx, uint8_t *hdr; uint8_t cmd; uint32_t wct_ofs; + NTSTATUS status; + size_t min_size = MIN_SMB_SIZE; - buflen = smb_len_nbt(buf); + buflen = smb_len_tcp(buf); taken = 0; hdr = buf + NBT_HDR_SIZE; - if (buflen < MIN_SMB_SIZE) { + status = smb1cli_pull_raw_error(hdr); + if (NT_STATUS_IS_ERR(status)) { + /* + * This is an ugly hack to support OS/2 + * which skips the byte_count in the DATA block + * on some error responses. + * + * See bug #9096 + */ + min_size -= sizeof(uint16_t); + } + + if (buflen < min_size) { return NT_STATUS_INVALID_NETWORK_RESPONSE; } @@ -1647,9 +1723,9 @@ static NTSTATUS smb1cli_inbuf_parse_chain(uint8_t *buf, TALLOC_CTX *mem_ctx, size_t needed; /* - * we need at least WCT and BCC + * we need at least WCT */ - needed = sizeof(uint8_t) + sizeof(uint16_t); + needed = sizeof(uint8_t); if (len < needed) { DEBUG(10, ("%s: %d bytes left, expected at least %d\n", __location__, (int)len, (int)needed)); @@ -1667,6 +1743,46 @@ static NTSTATUS smb1cli_inbuf_parse_chain(uint8_t *buf, TALLOC_CTX *mem_ctx, goto inval; } + if ((num_iov == 1) && + (len == needed) && + NT_STATUS_IS_ERR(status)) + { + /* + * This is an ugly hack to support OS/2 + * which skips the byte_count in the DATA block + * on some error responses. + * + * See bug #9096 + */ + iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec, + num_iov + 2); + if (iov_tmp == NULL) { + TALLOC_FREE(iov); + return NT_STATUS_NO_MEMORY; + } + iov = iov_tmp; + cur = &iov[num_iov]; + num_iov += 2; + + cur[0].iov_len = 0; + cur[0].iov_base = hdr + (wct_ofs + sizeof(uint8_t)); + cur[1].iov_len = 0; + cur[1].iov_base = cur[0].iov_base; + + taken += needed; + break; + } + + /* + * we need at least BCC + */ + needed += sizeof(uint16_t); + if (len < needed) { + DEBUG(10, ("%s: %d bytes left, expected at least %d\n", + __location__, (int)len, (int)needed)); + goto inval; + } + /* * Now we check if the specified bytes are there */ @@ -1775,7 +1891,8 @@ static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn, uint8_t cmd; uint16_t mid; bool oplock_break; - const uint8_t *inhdr = inbuf + NBT_HDR_SIZE; + uint8_t *inhdr = inbuf + NBT_HDR_SIZE; + size_t len = smb_len_tcp(inbuf); struct iovec *iov = NULL; int num_iov = 0; struct tevent_req **chain = NULL; @@ -1803,8 +1920,8 @@ static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn, } } - state->smb1.recv_iov[0].iov_base = (void *)(inbuf + NBT_HDR_SIZE); - state->smb1.recv_iov[0].iov_len = smb_len_nbt(inbuf); + state->smb1.recv_iov[0].iov_base = (void *)(inhdr); + state->smb1.recv_iov[0].iov_len = len; ZERO_STRUCT(state->smb1.recv_iov[1]); ZERO_STRUCT(state->smb1.recv_iov[2]); @@ -1851,6 +1968,8 @@ static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn, nt_errstr(status))); return status; } + inhdr = inbuf + NBT_HDR_SIZE; + len = smb_len_nbt(inbuf); } mid = SVAL(inhdr, HDR_MID); @@ -1872,7 +1991,7 @@ static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn, /* * Paranoia checks that this is really an oplock break request. */ - oplock_break = (smb_len_nbt(inbuf) == 51); /* hdr + 8 words */ + oplock_break = (len == 51); /* hdr + 8 words */ oplock_break &= ((CVAL(inhdr, HDR_FLG) & FLAG_REPLY) == 0); oplock_break &= (CVAL(inhdr, HDR_COM) == SMBlockingX); oplock_break &= (SVAL(inhdr, HDR_VWV+VWV(6)) == 0); @@ -1889,7 +2008,7 @@ static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn, if (!oplock_break /* oplock breaks are not signed */ && !smb_signing_check_pdu(conn->smb1.signing, - inbuf, state->smb1.seqnum+1)) { + inhdr, len, state->smb1.seqnum+1)) { DEBUG(10, ("cli_check_sign_mac failed\n")); return NT_STATUS_ACCESS_DENIED; } @@ -1905,6 +2024,17 @@ static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn, cmd = CVAL(inhdr, HDR_COM); status = smb1cli_pull_raw_error(inhdr); + if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED) && + (state->session != NULL) && state->session->disconnect_expired) + { + /* + * this should be a short term hack + * until the upper layers have implemented + * re-authentication. + */ + return status; + } + if (state->smb1.chained_requests == NULL) { if (num_iov != 3) { return NT_STATUS_INVALID_NETWORK_RESPONSE; @@ -2347,6 +2477,37 @@ bool smbXcli_conn_has_async_calls(struct smbXcli_conn *conn) || (talloc_array_length(conn->pending) != 0)); } +bool smbXcli_conn_dfs_supported(struct smbXcli_conn *conn) +{ + if (conn->protocol >= PROTOCOL_SMB2_02) { + return (smb2cli_conn_server_capabilities(conn) & SMB2_CAP_DFS); + } + + return (smb1cli_conn_capabilities(conn) & CAP_DFS); +} + +bool smb2cli_conn_req_possible(struct smbXcli_conn *conn, uint32_t *max_dyn_len) +{ + uint16_t credits = 1; + + if (conn->smb2.cur_credits == 0) { + if (max_dyn_len != NULL) { + *max_dyn_len = 0; + } + return false; + } + + if (conn->smb2.server.capabilities & SMB2_CAP_LARGE_MTU) { + credits = conn->smb2.cur_credits; + } + + if (max_dyn_len != NULL) { + *max_dyn_len = credits * 65536; + } + + return true; +} + uint32_t smb2cli_conn_server_capabilities(struct smbXcli_conn *conn) { return conn->smb2.server.capabilities; @@ -2406,12 +2567,18 @@ static bool smb2cli_req_cancel(struct tevent_req *req) 0, /* timeout */ tcon, session, fixed, fixed_len, - NULL, 0); + NULL, 0, 0); if (subreq == NULL) { return false; } substate = tevent_req_data(subreq, struct smbXcli_req_state); + /* + * clear everything but the SMB2_HDR_FLAG_ASYNC flag + * e.g. if SMB2_HDR_FLAG_CHAINED is set we get INVALID_PARAMETER back + */ + flags &= SMB2_HDR_FLAG_ASYNC; + if (flags & SMB2_HDR_FLAG_ASYNC) { mid = 0; } @@ -2449,7 +2616,8 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx, const uint8_t *fixed, uint16_t fixed_len, const uint8_t *dyn, - uint32_t dyn_len) + uint32_t dyn_len, + uint32_t max_dyn_len) { struct tevent_req *req; struct smbXcli_req_state *state; @@ -2522,6 +2690,7 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx, state->smb2.fixed_len = fixed_len; state->smb2.dyn = dyn; state->smb2.dyn_len = dyn_len; + state->smb2.max_dyn_len = max_dyn_len; if (state->smb2.should_encrypt) { SIVAL(state->smb2.transform, SMB2_TF_PROTOCOL_ID, SMB2_TF_MAGIC); @@ -2585,14 +2754,17 @@ NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs, struct tevent_req *subreq; struct iovec *iov; int i, num_iov, nbt_len; + int tf_iov = -1; + const DATA_BLOB *encryption_key = NULL; + uint64_t encryption_session_id = 0; /* - * 1 for the nbt length - * per request: TRANSFORM, HDR, fixed, dyn, padding + * 1 for the nbt length, optional TRANSFORM + * per request: HDR, fixed, dyn, padding * -1 because the last one does not need padding */ - iov = talloc_array(reqs[0], struct iovec, 1 + 5*num_reqs - 1); + iov = talloc_array(reqs[0], struct iovec, 1 + 1 + 4*num_reqs - 1); if (iov == NULL) { return NT_STATUS_NO_MEMORY; } @@ -2600,8 +2772,65 @@ NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs, num_iov = 1; nbt_len = 0; + /* + * the session of the first request that requires encryption + * specifies the encryption key. + */ + for (i=0; iconn)) { + return NT_STATUS_CONNECTION_DISCONNECTED; + } + + if ((state->conn->protocol != PROTOCOL_NONE) && + (state->conn->protocol < PROTOCOL_SMB2_02)) { + return NT_STATUS_REVISION_MISMATCH; + } + + if (state->session == NULL) { + continue; + } + + if (!state->smb2.should_encrypt) { + continue; + } + + encryption_key = &state->session->smb2->encryption_key; + if (encryption_key->length == 0) { + return NT_STATUS_INVALID_PARAMETER_MIX; + } + + encryption_session_id = state->session->smb2->session_id; + + tf_iov = num_iov; + iov[num_iov].iov_base = state->smb2.transform; + iov[num_iov].iov_len = sizeof(state->smb2.transform); + num_iov += 1; + + SBVAL(state->smb2.transform, SMB2_TF_PROTOCOL_ID, SMB2_TF_MAGIC); + SBVAL(state->smb2.transform, SMB2_TF_NONCE, + state->session->smb2->nonce_low); + SBVAL(state->smb2.transform, SMB2_TF_NONCE+8, + state->session->smb2->nonce_high); + SBVAL(state->smb2.transform, SMB2_TF_SESSION_ID, + encryption_session_id); + + state->session->smb2->nonce_low += 1; + if (state->session->smb2->nonce_low == 0) { + state->session->smb2->nonce_high += 1; + state->session->smb2->nonce_low += 1; + } + + nbt_len += SMB2_TF_HDR_SIZE; + break; + } + for (i=0; iconn->smb2.server.capabilities & SMB2_CAP_LARGE_MTU) { - charge = (MAX(state->smb2.dyn_len, 1) - 1)/ 65536 + 1; + uint32_t max_dyn_len = 1; + + max_dyn_len = MAX(max_dyn_len, state->smb2.dyn_len); + max_dyn_len = MAX(max_dyn_len, state->smb2.max_dyn_len); + + charge = (max_dyn_len - 1)/ 65536 + 1; } else { charge = 1; } @@ -2671,7 +2904,7 @@ NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs, SBVAL(state->smb2.hdr, SMB2_HDR_MESSAGE_ID, mid); skip_credits: - if (state->session) { + if (state->session && encryption_key == NULL) { /* * We prefer the channel signing key if it is * already there. @@ -2695,17 +2928,6 @@ skip_credits: if (signing_key && signing_key->length == 0) { signing_key = NULL; } - - if (state->smb2.should_encrypt) { - encryption_key = &state->session->smb2->encryption_key; - } - } - - if (encryption_key) { - tf_iov = num_iov; - iov[num_iov].iov_base = state->smb2.transform; - iov[num_iov].iov_len = sizeof(state->smb2.transform); - num_iov += 1; } hdr_iov = num_iov; @@ -2738,56 +2960,9 @@ skip_credits: SIVAL(state->smb2.hdr, SMB2_HDR_NEXT_COMMAND, reqlen); } - if (encryption_key) { - NTSTATUS status; - uint8_t *buf; - int vi; - - SBVAL(state->smb2.transform, SMB2_TF_NONCE, - state->session->smb2->nonce_low); - SBVAL(state->smb2.transform, SMB2_TF_NONCE+8, - state->session->smb2->nonce_high); - - state->session->smb2->nonce_low += 1; - if (state->session->smb2->nonce_low == 0) { - state->session->smb2->nonce_high += 1; - state->session->smb2->nonce_low += 1; - } - - SBVAL(state->smb2.transform, SMB2_TF_MSG_SIZE, - reqlen); - - buf = talloc_array(iov, uint8_t, reqlen); - if (buf == NULL) { - return NT_STATUS_NO_MEMORY; - } - - reqlen += SMB2_TF_HDR_SIZE; - - /* - * We copy the buffers before encrypting them, - * this is at least currently needed for the - * to keep state->smb2.hdr. - * - * Also the callers may expect there buffers - * to be const. - */ - for (vi = hdr_iov; vi < num_iov; vi++) { - struct iovec *v = &iov[vi]; - const uint8_t *o = (const uint8_t *)v->iov_base; - - memcpy(buf, o, v->iov_len); - v->iov_base = (void *)buf; - buf += v->iov_len; - } + state->smb2.encryption_session_id = encryption_session_id; - status = smb2_signing_encrypt_pdu(*encryption_key, - state->session->conn->protocol, - &iov[tf_iov], num_iov - tf_iov); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - } else if (signing_key) { + if (signing_key != NULL) { NTSTATUS status; status = smb2_signing_sign_pdu(*signing_key, @@ -2811,6 +2986,42 @@ skip_credits: iov[0].iov_base = state->length_hdr; iov[0].iov_len = sizeof(state->length_hdr); + if (encryption_key != NULL) { + NTSTATUS status; + size_t buflen = nbt_len - SMB2_TF_HDR_SIZE; + uint8_t *buf; + int vi; + + buf = talloc_array(iov, uint8_t, buflen); + if (buf == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* + * We copy the buffers before encrypting them, + * this is at least currently needed for the + * to keep state->smb2.hdr. + * + * Also the callers may expect there buffers + * to be const. + */ + for (vi = tf_iov + 1; vi < num_iov; vi++) { + struct iovec *v = &iov[vi]; + const uint8_t *o = (const uint8_t *)v->iov_base; + + memcpy(buf, o, v->iov_len); + v->iov_base = (void *)buf; + buf += v->iov_len; + } + + status = smb2_signing_encrypt_pdu(*encryption_key, + state->conn->protocol, + &iov[tf_iov], num_iov - tf_iov); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + if (state->conn->dispatch_incoming == NULL) { state->conn->dispatch_incoming = smb2cli_conn_dispatch_incoming; } @@ -2845,7 +3056,8 @@ struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx, const uint8_t *fixed, uint16_t fixed_len, const uint8_t *dyn, - uint32_t dyn_len) + uint32_t dyn_len, + uint32_t max_dyn_len) { struct tevent_req *req; NTSTATUS status; @@ -2854,7 +3066,9 @@ struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx, additional_flags, clear_flags, timeout_msec, tcon, session, - fixed, fixed_len, dyn, dyn_len); + fixed, fixed_len, + dyn, dyn_len, + max_dyn_len); if (req == NULL) { return NULL; } @@ -2899,6 +3113,9 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn, int num_iov = 0; size_t taken = 0; uint8_t *first_hdr = buf; + size_t verified_buflen = 0; + uint8_t *tf = NULL; + size_t tf_len = 0; iov = talloc_array(mem_ctx, struct iovec, num_iov); if (iov == NULL) { @@ -2906,8 +3123,6 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn, } while (taken < buflen) { - uint8_t *tf = NULL; - size_t tf_len = 0; size_t len = buflen - taken; uint8_t *hdr = first_hdr + taken; struct iovec *cur; @@ -2916,6 +3131,13 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn, uint16_t body_size; struct iovec *iov_tmp; + if (verified_buflen > taken) { + len = verified_buflen - taken; + } else { + tf = NULL; + tf_len = 0; + } + if (len < 4) { DEBUG(10, ("%d bytes left, expected at least %d\n", (int)len, 4)); @@ -2925,6 +3147,7 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn, struct smbXcli_session *s; uint64_t uid; struct iovec tf_iov[2]; + size_t enc_len; NTSTATUS status; if (len < SMB2_TF_HDR_SIZE) { @@ -2937,9 +3160,16 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn, taken += tf_len; hdr = first_hdr + taken; - len = IVAL(tf, SMB2_TF_MSG_SIZE); + enc_len = IVAL(tf, SMB2_TF_MSG_SIZE); uid = BVAL(tf, SMB2_TF_SESSION_ID); + if (len < SMB2_TF_HDR_SIZE + enc_len) { + DEBUG(10, ("%d bytes left, expected at least %d\n", + (int)len, + (int)(SMB2_TF_HDR_SIZE + enc_len))); + goto inval; + } + s = conn->sessions; for (; s; s = s->next) { if (s->smb2->session_id != uid) { @@ -2957,7 +3187,7 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn, tf_iov[0].iov_base = (void *)tf; tf_iov[0].iov_len = tf_len; tf_iov[1].iov_base = (void *)hdr; - tf_iov[1].iov_len = len; + tf_iov[1].iov_len = enc_len; status = smb2_signing_decrypt_pdu(s->smb2->decryption_key, conn->protocol, @@ -2966,6 +3196,9 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn, TALLOC_FREE(iov); return status; } + + verified_buflen = taken + enc_len; + len = enc_len; } /* @@ -2999,9 +3232,6 @@ static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn, if (next_command_ofs > full_size) { goto inval; } - if (tf && next_command_ofs < len) { - goto inval; - } full_size = next_command_ofs; } if (body_size < 2) { @@ -3098,6 +3328,7 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn, uint32_t new_credits; struct smbXcli_session *session = NULL; const DATA_BLOB *signing_key = NULL; + bool was_encrypted = false; new_credits = conn->smb2.cur_credits; new_credits += credits; @@ -3215,6 +3446,26 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn, } } + if (cur[0].iov_len == SMB2_TF_HDR_SIZE) { + const uint8_t *tf = (const uint8_t *)cur[0].iov_base; + uint64_t uid = BVAL(tf, SMB2_TF_SESSION_ID); + + /* + * If the response was encrypted in a SMB2_TRANSFORM + * pdu, which belongs to the correct session, + * we do not need to do signing checks + * + * It could be the session the response belongs to + * or the session that was used to encrypt the + * SMB2_TRANSFORM request. + */ + if ((session && session->smb2->session_id == uid) || + (state->smb2.encryption_session_id == uid)) { + signing_key = NULL; + was_encrypted = true; + } + } + if (NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED)) { /* * if the server returns NT_STATUS_USER_SESSION_DELETED @@ -3222,9 +3473,24 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn, * propagate the NT_STATUS_USER_SESSION_DELETED * status to the caller. */ + state->smb2.signing_skipped = true; signing_key = NULL; - } else if (state->smb2.should_encrypt) { - if (cur[0].iov_len != SMB2_TF_HDR_SIZE) { + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + /* + * if the server returns + * NT_STATUS_INVALID_PARAMETER + * the response might not be encrypted. + */ + if (state->smb2.should_encrypt && !was_encrypted) { + state->smb2.signing_skipped = true; + signing_key = NULL; + } + } + + if (state->smb2.should_encrypt && !was_encrypted) { + if (!state->smb2.signing_skipped) { return NT_STATUS_ACCESS_DENIED; } } @@ -3282,6 +3548,17 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn, } } + if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED) && + (session != NULL) && session->disconnect_expired) + { + /* + * this should be a short term hack + * until the upper layers have implemented + * re-authentication. + */ + return status; + } + smbXcli_req_unset_pending(req); /* @@ -3433,6 +3710,7 @@ static const struct { {PROTOCOL_SMB2_22, SMB2_DIALECT_REVISION_222}, {PROTOCOL_SMB2_24, SMB2_DIALECT_REVISION_224}, {PROTOCOL_SMB3_00, SMB3_DIALECT_REVISION_300}, + {PROTOCOL_SMB3_02, SMB3_DIALECT_REVISION_302}, }; struct smbXcli_negprot_state { @@ -3857,6 +4135,15 @@ static void smbXcli_negprot_smb1_done(struct tevent_req *subreq) if (server_security_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) { server_signing = "supported"; server_allowed = true; + } else if (conn->mandatory_signing) { + /* + * We have mandatory signing as client + * lets assume the server will look at our + * FLAGS2_SMB_SECURITY_SIGNATURES_REQUIRED + * flag in the session setup + */ + server_signing = "not announced"; + server_allowed = true; } if (server_security_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) { server_signing = "required"; @@ -4046,7 +4333,8 @@ static struct tevent_req *smbXcli_negprot_smb2_subreq(struct smbXcli_negprot_sta state->timeout_msec, NULL, NULL, /* tcon, session */ state->smb2.fixed, sizeof(state->smb2.fixed), - state->smb2.dyn, dialect_count*2); + state->smb2.dyn, dialect_count*2, + UINT16_MAX); /* max_dyn_len */ } static void smbXcli_negprot_smb2_done(struct tevent_req *subreq) @@ -4206,9 +4494,14 @@ static NTSTATUS smbXcli_negprot_dispatch_incoming(struct smbXcli_conn *conn, /* * we got an SMB2 answer, which consumed sequence number 0 - * so we need to use 1 as the next one + * so we need to use 1 as the next one. + * + * we also need to set the current credits to 0 + * as we consumed the initial one. The SMB2 answer + * hopefully grant us a new credit. */ conn->smb2.mid = 1; + conn->smb2.cur_credits = 0; tevent_req_set_callback(subreq, smbXcli_negprot_smb2_done, req); conn->dispatch_incoming = smb2cli_conn_dispatch_incoming; return smb2cli_conn_dispatch_incoming(conn, tmp_mem, inbuf); @@ -4241,7 +4534,7 @@ NTSTATUS smbXcli_negprot(struct smbXcli_conn *conn, status = NT_STATUS_INVALID_PARAMETER_MIX; goto fail; } - ev = tevent_context_init(frame); + ev = samba_tevent_context_init(frame); if (ev == NULL) { goto fail; } @@ -4293,6 +4586,68 @@ struct smbXcli_session *smbXcli_session_create(TALLOC_CTX *mem_ctx, return session; } +struct smbXcli_session *smbXcli_session_copy(TALLOC_CTX *mem_ctx, + struct smbXcli_session *src) +{ + struct smbXcli_session *session; + + session = talloc_zero(mem_ctx, struct smbXcli_session); + if (session == NULL) { + return NULL; + } + session->smb2 = talloc_zero(session, struct smb2cli_session); + if (session->smb2 == NULL) { + talloc_free(session); + return NULL; + } + + session->conn = src->conn; + *session->smb2 = *src->smb2; + session->smb2_channel = src->smb2_channel; + session->disconnect_expired = src->disconnect_expired; + + DLIST_ADD_END(src->conn->sessions, session, struct smbXcli_session *); + talloc_set_destructor(session, smbXcli_session_destructor); + + return session; +} + + +NTSTATUS smbXcli_session_application_key(struct smbXcli_session *session, + TALLOC_CTX *mem_ctx, + DATA_BLOB *key) +{ + const DATA_BLOB *application_key; + + *key = data_blob_null; + + if (session->conn == NULL) { + return NT_STATUS_NO_USER_SESSION_KEY; + } + + if (session->conn->protocol >= PROTOCOL_SMB2_02) { + application_key = &session->smb2->application_key; + } else { + application_key = &session->smb1.application_key; + } + + if (application_key->length == 0) { + return NT_STATUS_NO_USER_SESSION_KEY; + } + + *key = data_blob_dup_talloc(mem_ctx, *application_key); + if (key->data == NULL) { + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +void smbXcli_session_set_disconnect_expired(struct smbXcli_session *session) +{ + session->disconnect_expired = true; +} + uint16_t smb1cli_session_current_id(struct smbXcli_session *session) { return session->smb1.session_id; @@ -4304,6 +4659,67 @@ void smb1cli_session_set_id(struct smbXcli_session *session, session->smb1.session_id = session_id; } +NTSTATUS smb1cli_session_set_session_key(struct smbXcli_session *session, + const DATA_BLOB _session_key) +{ + struct smbXcli_conn *conn = session->conn; + uint8_t session_key[16]; + + if (conn == NULL) { + return NT_STATUS_INVALID_PARAMETER_MIX; + } + + if (session->smb1.application_key.length != 0) { + /* + * TODO: do not allow this... + * + * return NT_STATUS_INVALID_PARAMETER_MIX; + */ + data_blob_clear_free(&session->smb1.application_key); + session->smb1.protected_key = false; + } + + if (_session_key.length == 0) { + return NT_STATUS_OK; + } + + ZERO_STRUCT(session_key); + memcpy(session_key, _session_key.data, + MIN(_session_key.length, sizeof(session_key))); + + session->smb1.application_key = data_blob_talloc(session, + session_key, + sizeof(session_key)); + ZERO_STRUCT(session_key); + if (session->smb1.application_key.data == NULL) { + return NT_STATUS_NO_MEMORY; + } + + session->smb1.protected_key = false; + + return NT_STATUS_OK; +} + +NTSTATUS smb1cli_session_protect_session_key(struct smbXcli_session *session) +{ + if (session->smb1.protected_key) { + /* already protected */ + return NT_STATUS_OK; + } + + if (session->smb1.application_key.length != 16) { + return NT_STATUS_INVALID_PARAMETER_MIX; + } + + smb_key_derivation(session->smb1.application_key.data, + session->smb1.application_key.length, + session->smb1.application_key.data); + + session->smb1.protected_key = true; + + return NT_STATUS_OK; +} + uint8_t smb2cli_session_security_mode(struct smbXcli_session *session) { struct smbXcli_conn *conn = session->conn; @@ -4331,24 +4747,6 @@ uint16_t smb2cli_session_get_flags(struct smbXcli_session *session) return session->smb2->session_flags; } -NTSTATUS smb2cli_session_application_key(struct smbXcli_session *session, - TALLOC_CTX *mem_ctx, - DATA_BLOB *key) -{ - *key = data_blob_null; - - if (session->smb2->application_key.length == 0) { - return NT_STATUS_NO_USER_SESSION_KEY; - } - - *key = data_blob_dup_talloc(mem_ctx, session->smb2->application_key); - if (key->data == NULL) { - return NT_STATUS_NO_MEMORY; - } - - return NT_STATUS_OK; -} - void smb2cli_session_set_id_and_flags(struct smbXcli_session *session, uint64_t session_id, uint16_t session_flags) @@ -4369,12 +4767,18 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, struct smbXcli_conn *conn = session->conn; uint16_t no_sign_flags; uint8_t session_key[16]; + bool check_signature = true; + uint32_t hdr_flags; NTSTATUS status; if (conn == NULL) { return NT_STATUS_INVALID_PARAMETER_MIX; } + if (recv_iov[0].iov_len != SMB2_HDR_BODY) { + return NT_STATUS_INVALID_PARAMETER_MIX; + } + no_sign_flags = SMB2_SESSION_FLAG_IS_GUEST | SMB2_SESSION_FLAG_IS_NULL; if (session->smb2->session_flags & no_sign_flags) { @@ -4466,11 +4870,30 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, return NT_STATUS_NO_MEMORY; } - status = smb2_signing_check_pdu(session->smb2_channel.signing_key, - session->conn->protocol, - recv_iov, 3); - if (!NT_STATUS_IS_OK(status)) { - return status; + check_signature = conn->mandatory_signing; + + hdr_flags = IVAL(recv_iov[0].iov_base, SMB2_HDR_FLAGS); + if (hdr_flags & SMB2_HDR_FLAG_SIGNED) { + /* + * Sadly some vendors don't sign the + * final SMB2 session setup response + * + * At least Windows and Samba are always doing this + * if there's a session key available. + * + * We only check the signature if it's mandatory + * or SMB2_HDR_FLAG_SIGNED is provided. + */ + check_signature = true; + } + + if (check_signature) { + status = smb2_signing_check_pdu(session->smb2_channel.signing_key, + session->conn->protocol, + recv_iov, 3); + if (!NT_STATUS_IS_OK(status)) { + return status; + } } session->smb2->should_sign = false; @@ -4585,6 +5008,27 @@ NTSTATUS smb2cli_session_set_channel_key(struct smbXcli_session *session, return NT_STATUS_OK; } +NTSTATUS smb2cli_session_encryption_on(struct smbXcli_session *session) +{ + if (session->smb2->should_encrypt) { + return NT_STATUS_OK; + } + + if (session->conn->protocol < PROTOCOL_SMB2_24) { + return NT_STATUS_NOT_SUPPORTED; + } + + if (!(session->conn->smb2.server.capabilities & SMB2_CAP_ENCRYPTION)) { + return NT_STATUS_NOT_SUPPORTED; + } + + if (session->smb2->signing_key.data == NULL) { + return NT_STATUS_NOT_SUPPORTED; + } + session->smb2->should_encrypt = true; + return NT_STATUS_OK; +} + struct smbXcli_tcon *smbXcli_tcon_create(TALLOC_CTX *mem_ctx) { struct smbXcli_tcon *tcon; @@ -4597,6 +5041,38 @@ struct smbXcli_tcon *smbXcli_tcon_create(TALLOC_CTX *mem_ctx) return tcon; } +void smbXcli_tcon_set_fs_attributes(struct smbXcli_tcon *tcon, + uint32_t fs_attributes) +{ + tcon->fs_attributes = fs_attributes; +} + +uint32_t smbXcli_tcon_get_fs_attributes(struct smbXcli_tcon *tcon) +{ + return tcon->fs_attributes; +} + +bool smbXcli_tcon_is_dfs_share(struct smbXcli_tcon *tcon) +{ + if (tcon == NULL) { + return false; + } + + if (tcon->is_smb1) { + if (tcon->smb1.optional_support & SMB_SHARE_IN_DFS) { + return true; + } + + return false; + } + + if (tcon->smb2.capabilities & SMB2_SHARE_CAP_DFS) { + return true; + } + + return false; +} + uint16_t smb1cli_tcon_current_id(struct smbXcli_tcon *tcon) { return tcon->smb1.tcon_id; @@ -4604,6 +5080,7 @@ uint16_t smb1cli_tcon_current_id(struct smbXcli_tcon *tcon) void smb1cli_tcon_set_id(struct smbXcli_tcon *tcon, uint16_t tcon_id) { + tcon->is_smb1 = true; tcon->smb1.tcon_id = tcon_id; } @@ -4615,6 +5092,8 @@ bool smb1cli_tcon_set_values(struct smbXcli_tcon *tcon, const char *service, const char *fs_type) { + tcon->is_smb1 = true; + tcon->fs_attributes = 0; tcon->smb1.tcon_id = tcon_id; tcon->smb1.optional_support = optional_support; tcon->smb1.maximal_access = maximal_access; @@ -4653,6 +5132,8 @@ void smb2cli_tcon_set_values(struct smbXcli_tcon *tcon, uint32_t capabilities, uint32_t maximal_access) { + tcon->is_smb1 = false; + tcon->fs_attributes = 0; tcon->smb2.tcon_id = tcon_id; tcon->smb2.type = type; tcon->smb2.flags = flags; @@ -4671,3 +5152,8 @@ void smb2cli_tcon_set_values(struct smbXcli_tcon *tcon, tcon->smb2.should_encrypt = true; } } + +bool smb2cli_tcon_is_encryption_on(struct smbXcli_tcon *tcon) +{ + return tcon->smb2.should_encrypt; +}