/* * 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)); }
/* * 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); }