Exemple #1
0
/*
 * smb2_sign_reply
 *
 * Calculates MAC signature for the given mbuf chain,
 * and write it to the signature field in the mbuf.
 *
 */
void
smb2_sign_reply(smb_request_t *sr)
{
	uint8_t reply_sig[SMB2_SIG_SIZE];
	struct mbuf_chain tmp_mbc;
	smb_session_t *s = sr->session;
	smb_user_t *u = sr->uid_user;
	int hdr_off, msg_len;

	if (u == NULL)
		return;
	if (s->sign_calc == NULL)
		return;

	msg_len = sr->reply.chain_offset - sr->smb2_reply_hdr;
	(void) MBC_SHADOW_CHAIN(&tmp_mbc, &sr->reply,
	    sr->smb2_reply_hdr, msg_len);

	/*
	 * Calculate the MAC signature for this reply.
	 * smb2_sign_calc() or smb3_sign_calc()
	 */
	if (s->sign_calc(sr, &tmp_mbc, reply_sig) != 0)
		return;

	/*
	 * Poke the signature into the response.
	 */
	hdr_off = sr->smb2_reply_hdr + SMB2_SIG_OFFS;
	(void) smb_mbc_poke(&sr->reply, hdr_off, "#c",
	    SMB2_SIG_SIZE, reply_sig);
}
/*
 * smb2_sign_reply
 *
 * Calculates MAC signature for the given mbuf chain,
 * and write it to the signature field in the mbuf.
 *
 */
void
smb2_sign_reply(smb_request_t *sr)
{
	uint8_t digest[SHA256_DIGEST_LENGTH];
	struct mbuf_chain tmp_mbc;
	struct smb_sign *sign = &sr->session->signing;
	int hdr_off, msg_len;

	msg_len = sr->reply.chain_offset - sr->smb2_reply_hdr;
	(void) MBC_SHADOW_CHAIN(&tmp_mbc, &sr->reply,
	    sr->smb2_reply_hdr, msg_len);

	/*
	 * Calculate MAC signature
	 */
	if (smb2_sign_calc(&tmp_mbc, sign, digest) != 0)
		return;

	/*
	 * Poke the signature into the response.
	 */
	hdr_off = sr->smb2_reply_hdr + SMB2_SIG_OFFS;
	(void) smb_mbc_poke(&sr->reply, hdr_off, "#c",
	    SMB2_SIG_SIZE, digest);
}
/*
 * Dispatch an async request using saved information.
 * See smb2sr_save_async and [MS-SMB2] 3.3.4.2
 */
void
smb2sr_do_async(smb_request_t *sr)
{
	smb2_async_req_t *ar;
	int rc;

	/*
	 * Restore what smb2_decode_header found.
	 */
	ar = sr->sr_async_req;
	sr->smb2_cmd_hdr   = ar->ar_cmd_hdr;
	sr->smb2_cmd_code  = ar->ar_cmd_code;
	sr->smb2_hdr_flags = ar->ar_hdr_flags;
	sr->smb2_async_id  = (uintptr_t)ar;
	sr->smb2_messageid = ar->ar_messageid;
	sr->smb_pid = ar->ar_pid;
	sr->smb_tid = ar->ar_tid;
	sr->smb2_status = 0;

	/*
	 * Async requests don't grant credits, because any credits
	 * should have gone out with the interim reply.
	 */
	sr->smb2_credit_response = 0;

	/*
	 * Setup input mbuf_chain
	 */
	ASSERT(ar->ar_cmd_len >= SMB2_HDR_SIZE);
	(void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
	    sr->smb2_cmd_hdr + SMB2_HDR_SIZE,
	    ar->ar_cmd_len - SMB2_HDR_SIZE);

	/*
	 * Setup output mbuf_chain
	 */
	MBC_FLUSH(&sr->reply);
	sr->smb2_reply_hdr = sr->reply.chain_offset;
	(void) smb_mbc_encodef(&sr->reply, "#.", SMB2_HDR_SIZE);

	/*
	 * Call the common dispatch code, but override the
	 * command handler function with the async handler
	 * (ar->ar_func) which will be used instead of the
	 * normal handler from the dispatch table.
	 * The SMB signature was already checked.
	 */
	rc = smb2sr_dispatch(sr, ar->ar_func);
	if (rc != 0 && sr->smb2_status == 0)
		sr->smb2_status = NT_STATUS_INTERNAL_ERROR;

	/*
	 * Overwrite the SMB2 header for the response of
	 * this command (possibly part of a compound).
	 */
	sr->smb2_hdr_flags |= SMB2_FLAGS_SERVER_TO_REDIR;
	sr->smb2_next_reply = 0;
	(void) smb2_encode_header(sr, B_TRUE);

	if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED)
		smb2_sign_reply(sr);

	/*
	 * An async reply goes alone (no compound).
	 */
	smb2_send_reply(sr);

	/*
	 * Done.  Unlink and free.
	 */
	sr->sr_async_req = NULL;
	kmem_free(ar, sizeof (*ar));
}
Exemple #4
0
/*
 * SMB Negotiate gets special handling.  This is called directly by
 * the reader thread (see smbsr_newrq_initial) with what _should_ be
 * an SMB1 Negotiate.  Only the "\ffSMB" header has been checked
 * when this is called, so this needs to check the SMB command,
 * if it's Negotiate execute it, then send the reply, etc.
 *
 * Since this is called directly from the reader thread, we
 * know this is the only thread currently using this session.
 * This has to duplicate some of what smb1sr_work does as a
 * result of bypassing the normal dispatch mechanism.
 *
 * The caller always frees this request.
 */
int
smb1_newrq_negotiate(smb_request_t *sr)
{
	smb_sdrc_t	sdrc;
	uint16_t	pid_hi, pid_lo;

	/*
	 * Decode the header
	 */
	if (smb_mbc_decodef(&sr->command, SMB_HEADER_ED_FMT,
	    &sr->smb_com,
	    &sr->smb_rcls,
	    &sr->smb_reh,
	    &sr->smb_err,
	    &sr->smb_flg,
	    &sr->smb_flg2,
	    &pid_hi,
	    sr->smb_sig,
	    &sr->smb_tid,
	    &pid_lo,
	    &sr->smb_uid,
	    &sr->smb_mid) != 0)
		return (-1);
	if (sr->smb_com != SMB_COM_NEGOTIATE)
		return (-1);

	sr->smb_pid = (pid_hi << 16) | pid_lo;

	/*
	 * Reserve space for the reply header.
	 */
	(void) smb_mbc_encodef(&sr->reply, "#.", SMB_HEADER_LEN);
	sr->first_smb_com = sr->smb_com;

	if (smb_mbc_decodef(&sr->command, "b", &sr->smb_wct) != 0)
		return (-1);
	(void) MBC_SHADOW_CHAIN(&sr->smb_vwv, &sr->command,
	    sr->command.chain_offset, sr->smb_wct * 2);

	if (smb_mbc_decodef(&sr->command, "#.w", sr->smb_wct*2, &sr->smb_bcc))
		return (-1);
	(void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
	    sr->command.chain_offset, sr->smb_bcc);

	sr->command.chain_offset += sr->smb_bcc;
	if (sr->command.chain_offset > sr->command.max_bytes)
		return (-1);

	/* Store pointers for later */
	sr->cur_reply_offset = sr->reply.chain_offset;

	sdrc = smb_pre_negotiate(sr);
	if (sdrc == SDRC_SUCCESS)
		sdrc = smb_com_negotiate(sr);
	smb_post_negotiate(sr);

	if (sdrc != SDRC_NO_REPLY)
		smbsr_send_reply(sr);
	if (sdrc == SDRC_DROP_VC)
		return (-1);

	return (0);
}
/*
 * smb2sr_work
 *
 * This function processes each SMB command in the current request
 * (which may be a compound request) building a reply containing
 * SMB reply messages, one-to-one with the SMB commands.  Some SMB
 * commands (change notify, blocking pipe read) may require both an
 * "interim response" and a later "async response" at completion.
 * In such cases, we'll encode the interim response in the reply
 * compound we're building, and put the (now async) command on a
 * list of commands that need further processing.  After we've
 * finished processing the commands in this compound and building
 * the compound reply, we'll send the compound reply, and finally
 * process the list of async commands.
 *
 * As we work our way through the compound request and reply,
 * we need to keep track of the bounds of the current request
 * and reply.  For the request, this uses an MBC_SHADOW_CHAIN
 * that begins at smb2_cmd_hdr.  The reply is appended to the
 * sr->reply chain starting at smb2_reply_hdr.
 *
 * This function must always free the smb request.
 */
void
smb2sr_work(struct smb_request *sr)
{
	smb_session_t		*session;
	uint32_t		msg_len;
	int			rc;
	boolean_t		disconnect = B_FALSE;

	session = sr->session;

	ASSERT(sr->tid_tree == 0);
	ASSERT(sr->uid_user == 0);
	ASSERT(sr->fid_ofile == 0);
	sr->smb_fid = (uint16_t)-1;

	/* temporary until we identify a user */
	sr->user_cr = zone_kcred();

	mutex_enter(&sr->sr_mutex);
	switch (sr->sr_state) {
	case SMB_REQ_STATE_SUBMITTED:
	case SMB_REQ_STATE_CLEANED_UP:
		sr->sr_state = SMB_REQ_STATE_ACTIVE;
		break;
	default:
		ASSERT(0);
		/* FALLTHROUGH */
	case SMB_REQ_STATE_CANCELED:
		goto complete_unlock_free;
	}
	mutex_exit(&sr->sr_mutex);

cmd_start:
	/*
	 * Reserve space for the reply header, and save the offset.
	 * The reply header will be overwritten later.
	 */
	sr->smb2_reply_hdr = sr->reply.chain_offset;
	(void) smb_mbc_encodef(&sr->reply, "#.", SMB2_HDR_SIZE);

	/*
	 * Decode the request header
	 *
	 * Most problems with decoding will result in the error
	 * STATUS_INVALID_PARAMETER.  If the decoding problem
	 * prevents continuing, we'll close the connection.
	 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
	 */
	sr->smb2_status = 0;
	sr->smb2_cmd_hdr = sr->command.chain_offset;
	if ((rc = smb2_decode_header(sr)) != 0) {
		cmn_err(CE_WARN, "clnt %s bad SMB2 header",
		    session->ip_addr_str);
		disconnect = B_TRUE;
		goto cleanup;
	}

	/*
	 * Figure out the length of data following the SMB2 header.
	 * It ends at either the next SMB2 header if there is one
	 * (smb2_next_command != 0) or at the end of the message.
	 */
	if (sr->smb2_next_command != 0) {
		/* [MS-SMB2] says this is 8-byte aligned */
		msg_len = sr->smb2_next_command;
		if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) ||
		    ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) {
			cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd",
			    session->ip_addr_str);
			disconnect = B_TRUE;
			goto cleanup;
		}
	} else {
		msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr;
	}

	/*
	 * Setup a shadow chain for this SMB2 command, starting
	 * with the header and ending at either the next command
	 * or the end of the message.  Note that we've already
	 * decoded the header, so chain_offset is now positioned
	 * at the end of the header.  The signing check needs the
	 * entire SMB2 command, so we'll shadow starting at the
	 * smb2_cmd_hdr offset.  After the signing check, we'll
	 * move chain_offset up to the end of the header.
	 */
	(void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
	    sr->smb2_cmd_hdr, msg_len);

	/*
	 * Verify SMB signature if signing is enabled and active now.
	 * [MS-SMB2] 3.3.5.2.4 Verifying the Signature
	 */
	if ((sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) != 0) {
		rc = smb2_sign_check_request(sr);
		if (rc != 0) {
			DTRACE_PROBE1(smb2__sign__check, smb_request_t, sr);
			if (session->signing.flags & SMB_SIGNING_CHECK) {
				smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED);
				goto cmd_finish;
			}
		}
	}

	/*
	 * Now that the signing check is done with smb_data,
	 * advance past the SMB2 header we decoded above.
	 * This leaves sr->smb_data correctly positioned
	 * for command-specific decoding in the dispatch
	 * function called next.
	 */
	sr->smb_data.chain_offset = sr->smb2_cmd_hdr + SMB2_HDR_SIZE;

	/*
	 * Default credit response.  Command handler may modify.
	 */
	sr->smb2_credit_response = sr->smb2_credit_request;

	/*
	 * Common dispatch (for sync & async)
	 */
	rc = smb2sr_dispatch(sr, NULL);
	switch (rc) {
	case SDRC_SUCCESS:
		break;
	default:
		/*
		 * SMB2 does not use the other dispatch return codes.
		 * If we see something else, log an event so we'll
		 * know something is returning bogus status codes.
		 * If you see these in the log, use dtrace to find
		 * the code returning something else.
		 */
#ifdef	DEBUG
		cmn_err(CE_NOTE, "smb2sr_dispatch -> 0x%x", rc);
#endif
		/* FALLTHROUGH */
	case SDRC_ERROR:
		if (sr->smb2_status == 0)
			sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
		break;
	case SDRC_DROP_VC:
		disconnect = B_TRUE;
		goto cleanup;
	}

	/*
	 * If there's a next command, figure out where it starts,
	 * and fill in the next command offset for the reply.
	 * Note: We sanity checked smb2_next_command above
	 * (the offset to the next command).  Similarly set
	 * smb2_next_reply as the offset to the next reply.
	 */
cmd_finish:
	if (sr->smb2_next_command != 0) {
		sr->command.chain_offset =
		    sr->smb2_cmd_hdr + sr->smb2_next_command;
		sr->smb2_next_reply =
		    sr->reply.chain_offset - sr->smb2_reply_hdr;
	} else {
		sr->smb2_next_reply = 0;
	}

	/*
	 * Overwrite the SMB2 header for the response of
	 * this command (possibly part of a compound).
	 */
	sr->smb2_hdr_flags |= SMB2_FLAGS_SERVER_TO_REDIR;
	(void) smb2_encode_header(sr, B_TRUE);

	if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED)
		smb2_sign_reply(sr);

	if (sr->smb2_next_command != 0)
		goto cmd_start;

	/*
	 * We've done all the commands in this compound.
	 * Send it out.
	 */
	smb2_send_reply(sr);

	/*
	 * If any of the requests "went async", process those now.
	 */
	if (sr->sr_async_req != NULL) {
		smb2sr_do_async(sr);
	}

cleanup:
	if (disconnect) {
		smb_rwx_rwenter(&session->s_lock, RW_WRITER);
		switch (session->s_state) {
		case SMB_SESSION_STATE_DISCONNECTED:
		case SMB_SESSION_STATE_TERMINATED:
			break;
		default:
			smb_soshutdown(session->sock);
			session->s_state = SMB_SESSION_STATE_DISCONNECTED;
			break;
		}
		smb_rwx_rwexit(&session->s_lock);
	}


	mutex_enter(&sr->sr_mutex);
complete_unlock_free:
	sr->sr_state = SMB_REQ_STATE_COMPLETED;
	mutex_exit(&sr->sr_mutex);

	smb_request_free(sr);
}