Ejemplo n.º 1
0
/*
 * smb_handle_write_raw
 *
 * Called from smb_session_daemon() when the SMB command is SMB_COM_WRITE_RAW.
 * Dispatches the command to the worker thread and waits until the worker
 * has completed processing the command.
 *
 * Returns 0 for success, non-zero for failure
 */
int
smb_handle_write_raw(smb_session_t *session, smb_request_t *sr)
{
	int	drop_reason = 0;

	/*
	 * Set flag to indicate that we are waiting for raw data.  The
	 * worker thread will actually retrieve the raw data directly
	 * from the socket.  This should be the only case when a worker
	 * thread reads from the session socket.  When the data is read
	 * the worker will clear the flag.
	 */
	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
	switch (session->s_state) {
	case SMB_SESSION_STATE_NEGOTIATED:
	case SMB_SESSION_STATE_OPLOCK_BREAKING:
		session->s_state = SMB_SESSION_STATE_WRITE_RAW_ACTIVE;
		smb_rwx_rwexit(&session->s_lock);
		smb_srqueue_waitq_enter(session->s_srqueue);
		sr->sr_state = SMB_REQ_STATE_SUBMITTED;
		(void) taskq_dispatch(session->s_server->sv_worker_pool,
		    smb_session_worker, sr, TQ_SLEEP);
		smb_rwx_rwenter(&session->s_lock, RW_READER);
		while (session->s_state == SMB_SESSION_STATE_WRITE_RAW_ACTIVE) {
			(void) smb_rwx_rwwait(&session->s_lock, -1);
		}
		drop_reason = session->s_write_raw_status;
		break;
	default:
		drop_reason = 21;
		break;
	}
	smb_rwx_rwexit(&session->s_lock);
	return (drop_reason);
}
Ejemplo n.º 2
0
smb_sdrc_t
smb_pre_write_raw(smb_request_t *sr)
{
	smb_rw_param_t *param;
	uint32_t off_low;
	uint32_t timeout;
	uint32_t off_high;
	uint16_t datalen;
	uint16_t total;
	int rc;

	param = smb_srm_zalloc(sr, sizeof (smb_rw_param_t));
	sr->arg.rw = param;
	param->rw_magic = SMB_RW_MAGIC;

	if (sr->smb_wct == 12) {
		rc = smbsr_decode_vwv(sr, "ww2.llw4.ww", &sr->smb_fid, &total,
		    &off_low, &timeout, &param->rw_mode, &datalen,
		    &param->rw_dsoff);

		param->rw_offset = (uint64_t)off_low;
		param->rw_dsoff -= 59;
	} else {
		rc = smbsr_decode_vwv(sr, "ww2.llw4.wwl", &sr->smb_fid, &total,
		    &off_low, &timeout, &param->rw_mode, &datalen,
		    &param->rw_dsoff, &off_high);

		param->rw_offset = ((uint64_t)off_high << 32) | off_low;
		param->rw_dsoff -= 63;
	}

	param->rw_count = (uint32_t)datalen;
	param->rw_total = (uint32_t)total;
	param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;

	DTRACE_SMB_2(op__WriteRaw__start, smb_request_t *, sr,
	    smb_rw_param_t *, sr->arg.rw);

	smb_rwx_rwenter(&sr->session->s_lock, RW_WRITER);

	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
Ejemplo n.º 3
0
/*
 * smb2_sign_begin
 * Handles both SMB2 & SMB3
 *
 * Get the mechanism info.
 * Intializes MAC key based on the user session key and store it in
 * the signing structure.  This begins signing on this session.
 */
int
smb2_sign_begin(smb_request_t *sr, smb_token_t *token)
{
	smb_session_t *s = sr->session;
	smb_user_t *u = sr->uid_user;
	struct smb_key *sign_key = &u->u_sign_key;
	int (*get_mech)(smb_sign_mech_t *);
	int (*sign_calc)(smb_request_t *, struct mbuf_chain *, uint8_t *);
	smb_sign_mech_t *mech;
	int rc;

	/*
	 * We should normally have a session key here because
	 * our caller filters out Anonymous and Guest logons.
	 * However, buggy clients could get us here without a
	 * session key, in which case we'll fail later when a
	 * request that requires signing can't be checked.
	 */
	if (token->tkn_ssnkey.val == NULL || token->tkn_ssnkey.len == 0)
		return (0);

	if (s->dialect >= SMB_VERS_3_0) {
		get_mech = smb3_cmac_getmech;
		sign_calc = smb3_sign_calc;
	} else {
		get_mech = smb2_hmac_getmech;
		sign_calc = smb2_sign_calc;
	}

	/*
	 * Session-level initialization (once per session)
	 * Get mech handle, sign_calc function
	 */
	smb_rwx_rwenter(&s->s_lock, RW_WRITER);
	if (s->sign_mech == NULL) {
		mech = kmem_zalloc(sizeof (*mech), KM_SLEEP);
		rc = get_mech(mech);
		if (rc != 0) {
			kmem_free(mech, sizeof (*mech));
			smb_rwx_rwexit(&s->s_lock);
			return (rc);
		}
		s->sign_mech = mech;
		s->sign_calc = sign_calc;
		s->sign_fini = smb2_sign_fini;
	}
	smb_rwx_rwexit(&s->s_lock);

	/*
	 * Compute and store the signing key, which lives in
	 * the user structure.
	 */
	sign_key->len = SMB2_SIG_SIZE;
	if (s->dialect >= SMB_VERS_3_0) {
		/*
		 * For SMB3, the signing key is a "KDF" hash of the
		 * sesion key.
		 */
		if (smb3_do_kdf(sign_key->key, &token->tkn_ssnkey) != 0)
			return (-1);
	} else {
		/*
		 * For SMB2, the signing key is just the first 16 bytes
		 * of the session key (truncated or padded with zeros).
		 * [MS-SMB2] 3.2.5.3.1
		 */
		bcopy(token->tkn_ssnkey.val, sign_key->key,
		    MIN(token->tkn_ssnkey.len, sign_key->len));
	}

	mutex_enter(&u->u_mutex);
	if (s->secmode & SMB2_NEGOTIATE_SIGNING_ENABLED)
		u->u_sign_flags |= SMB_SIGNING_ENABLED;
	if (s->secmode & SMB2_NEGOTIATE_SIGNING_REQUIRED)
		u->u_sign_flags |=
		    SMB_SIGNING_ENABLED | SMB_SIGNING_CHECK;
	mutex_exit(&u->u_mutex);

	/*
	 * If we just turned on signing, the current request
	 * (an SMB2 session setup) will have come in without
	 * SMB2_FLAGS_SIGNED (and not signed) but the response
	 * is is supposed to be signed. [MS-SMB2] 3.3.5.5
	 */
	if (u->u_sign_flags & SMB_SIGNING_ENABLED)
		sr->smb2_hdr_flags |= SMB2_FLAGS_SIGNED;

	return (0);
}
Ejemplo n.º 4
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);
}