/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smb_share.h>
#include <smbsrv/string.h>
#include <smbsrv/nmpipes.h>
#include <smbsrv/mailslot.h>
#include <smbsrv/winioctl.h>
/*
* count of bytes in server response packet
* except parameters and data. Note that setup
* word count is zero.
*/
#define RESP_HEADER_LEN 24
/*
* We started by using common functions for transaction/transaction2
* and transaction_secondary/transaction2_secondary because they
* are respectively so similar. However, it turned out to be a bad
* idea because of quirky differences. Be sure if you modify one
* of these four functions to check and see if the modification should
* be applied to its peer.
*/
static int smb_trans_ready(smb_xa_t *);
static smb_sdrc_t smb_trans_dispatch(smb_request_t *, smb_xa_t *);
static smb_sdrc_t smb_trans2_dispatch(smb_request_t *, smb_xa_t *);
smb_sdrc_t
smb_pre_transaction(smb_request_t *sr)
{
DTRACE_SMB_1(op__Transaction__start, smb_request_t *, sr);
return (SDRC_SUCCESS);
}
void
smb_post_transaction(smb_request_t *sr)
{
DTRACE_SMB_1(op__Transaction__done, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_transaction(smb_request_t *sr)
{
int rc;
unsigned char msrcnt, suwcnt;
uint16_t tpscnt, tdscnt, mprcnt, mdrcnt, flags;
uint16_t pscnt, psoff, dscnt, dsoff;
uint32_t timeo;
struct smb_xa *xa;
char *stn;
int ready;
if (!STYPE_ISIPC(sr->tid_tree->t_res_type)) {
smbsr_error(sr, 0, ERRDOS, ERRnoaccess);
return (SDRC_ERROR);
}
rc = smbsr_decode_vwv(sr, SMB_TRANSHDR_ED_FMT,
&tpscnt, &tdscnt, &mprcnt, &mdrcnt, &msrcnt, &flags,
&timeo, &pscnt, &psoff, &dscnt, &dsoff, &suwcnt);
if (rc != 0)
return (SDRC_ERROR);
xa = smb_xa_create(sr->session, sr, tpscnt, tdscnt, mprcnt, mdrcnt,
msrcnt, suwcnt);
if (xa == NULL) {
smbsr_error(sr, 0, ERRSRV, ERRnoroom);
return (SDRC_ERROR);
}
/* Should be some alignment stuff here in SMB? */
if (sr->smb_flg2 & SMB_FLAGS2_UNICODE) {
rc = smbsr_decode_data(sr, "%.U", sr, &stn);
} else {
rc = smbsr_decode_data(sr, "%s", sr, &stn);
}
if (rc != 0) {
smb_xa_rele(sr->session, xa);
return (SDRC_ERROR);
}
xa->xa_pipe_name = smb_mem_strdup(stn);
xa->smb_flags = flags;
xa->smb_timeout = timeo;
xa->req_disp_param = pscnt;
xa->req_disp_data = dscnt;
if (smb_mbc_copy(&xa->req_setup_mb, &sr->smb_vwv,
sr->smb_vwv.chain_offset, suwcnt * 2)) {
smb_xa_rele(sr->session, xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
if (smb_mbc_copy(&xa->req_param_mb, &sr->command, psoff, pscnt)) {
smb_xa_rele(sr->session, xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
if (smb_mbc_copy(&xa->req_data_mb, &sr->command, dsoff, dscnt)) {
smb_xa_rele(sr->session, xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
ready = smb_trans_ready(xa);
if (smb_xa_open(xa)) {
smb_xa_rele(sr->session, xa);
smbsr_error(sr, 0, ERRSRV, ERRsrverror);
return (SDRC_ERROR);
}
sr->r_xa = xa;
if (!ready) {
rc = smbsr_encode_empty_result(sr);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
if (!smb_xa_complete(xa)) {
smb_xa_close(xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
return (smb_trans_dispatch(sr, xa));
}
smb_sdrc_t
smb_pre_transaction_secondary(smb_request_t *sr)
{
DTRACE_SMB_1(op__TransactionSecondary__start, smb_request_t *, sr);
return (SDRC_SUCCESS);
}
void
smb_post_transaction_secondary(smb_request_t *sr)
{
DTRACE_SMB_1(op__TransactionSecondary__done, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_transaction_secondary(smb_request_t *sr)
{
uint16_t tpscnt, tdscnt, pscnt, psdisp;
uint16_t dscnt, dsoff, dsdisp, psoff;
smb_xa_t *xa;
int rc;
if ((xa = smbsr_lookup_xa(sr)) == 0) {
smbsr_error(sr, 0, ERRSRV, ERRsrverror);
return (SDRC_ERROR);
}
if (sr->session->signing.flags & SMB_SIGNING_ENABLED) {
if (smb_sign_check_secondary(sr, xa->reply_seqnum) != 0) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
ERRDOS, ERRnoaccess);
return (SDRC_ERROR);
}
}
if (xa->smb_com != SMB_COM_TRANSACTION) {
return (SDRC_DROP_VC);
}
rc = smbsr_decode_vwv(sr, SMB_TRANSSHDR_ED_FMT, &tpscnt, &tdscnt,
&pscnt, &psoff, &psdisp, &dscnt, &dsoff, &dsdisp);
if (rc != 0)
return (SDRC_ERROR);
mutex_enter(&xa->xa_mutex);
if (xa->smb_tpscnt > tpscnt)
xa->smb_tpscnt = tpscnt;
if (xa->smb_tdscnt > tdscnt)
xa->smb_tdscnt = tdscnt;
xa->req_disp_param = psdisp + pscnt;
xa->req_disp_data = dsdisp + dscnt;
/*
* The words psdisp, dsdisp, tell us what displacement
* into the entire trans parameter and data buffers
* where we should put the params & data that are
* delivered by this request. [MS-CIFS] says all the
* parameters and data SHOULD be sent sequentially, so
* so we can normally reassemble by simply appending.
* However, the components MAY come out of order, so
* check and set the current offset. This is rare,
* and we might like to know when this happens, so
* fire some static dtrace probes when it does.
*/
if (xa->req_param_mb.chain_offset != psdisp) {
DTRACE_PROBE2(trans_param_disp,
smb_xa_t *, xa, uint16_t, psdisp);
xa->req_param_mb.chain_offset = psdisp;
}
if (xa->req_data_mb.chain_offset != dsdisp) {
DTRACE_PROBE2(trans_data_disp,
smb_xa_t *, xa, uint16_t, dsdisp);
xa->req_data_mb.chain_offset = dsdisp;
}
if (smb_mbc_copy(&xa->req_param_mb, &sr->command, psoff, pscnt)) {
mutex_exit(&xa->xa_mutex);
smb_xa_close(xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
if (smb_mbc_copy(&xa->req_data_mb, &sr->command, dsoff, dscnt)) {
mutex_exit(&xa->xa_mutex);
smb_xa_close(xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
mutex_exit(&xa->xa_mutex);
if (!smb_trans_ready(xa))
return (SDRC_NO_REPLY);
if (!smb_xa_complete(xa))
return (SDRC_NO_REPLY);
return (smb_trans_dispatch(sr, xa));
}
smb_sdrc_t
smb_pre_ioctl(smb_request_t *sr)
{
DTRACE_SMB_1(op__Ioctl__start, smb_request_t *, sr);
return (SDRC_SUCCESS);
}
void
smb_post_ioctl(smb_request_t *sr)
{
DTRACE_SMB_1(op__Ioctl__done, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_ioctl(smb_request_t *sr)
{
uint16_t fid, category, function, tpscnt, tdscnt, mprcnt;
uint16_t mdrcnt, pscnt, pdoff, dscnt, dsoff;
uint32_t timeout;
int rc;
rc = smbsr_decode_vwv(sr, "wwwwwwwl2.wwww", &fid, &category, &function,
&tpscnt, &tdscnt, &mprcnt, &mdrcnt, &timeout, &pscnt,
&pdoff, &dscnt, &dsoff);
if (rc != 0)
return (SDRC_ERROR);
return (SDRC_NOT_IMPLEMENTED);
}
smb_sdrc_t
smb_pre_transaction2(smb_request_t *sr)
{
DTRACE_SMB_1(op__Transaction2__start, smb_request_t *, sr);
return (SDRC_SUCCESS);
}
void
smb_post_transaction2(smb_request_t *sr)
{
DTRACE_SMB_1(op__Transaction2__done, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_transaction2(struct smb_request *sr)
{
unsigned char msrcnt, suwcnt;
uint16_t tpscnt, tdscnt, mprcnt, mdrcnt, flags;
uint16_t pscnt, psoff, dscnt, dsoff;
uint32_t timeo;
smb_xa_t *xa;
int ready;
int rc;
rc = smbsr_decode_vwv(sr, SMB_TRANSHDR_ED_FMT, &tpscnt, &tdscnt,
&mprcnt, &mdrcnt, &msrcnt, &flags, &timeo, &pscnt, &psoff, &dscnt,
&dsoff, &suwcnt);
if (rc != 0)
return (SDRC_ERROR);
xa = smb_xa_create(sr->session, sr, tpscnt, tdscnt, mprcnt, mdrcnt,
msrcnt, suwcnt);
if (xa == 0) {
smbsr_error(sr, 0, ERRSRV, ERRnoroom);
return (SDRC_ERROR);
}
xa->smb_flags = flags;
xa->smb_timeout = timeo;
xa->req_disp_param = pscnt;
xa->req_disp_data = dscnt;
if (smb_mbc_copy(&xa->req_setup_mb, &sr->smb_vwv,
sr->smb_vwv.chain_offset, suwcnt*2)) {
smb_xa_rele(sr->session, xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
if (smb_mbc_copy(&xa->req_param_mb, &sr->command, psoff, pscnt)) {
smb_xa_rele(sr->session, xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
if (smb_mbc_copy(&xa->req_data_mb, &sr->command, dsoff, dscnt)) {
smb_xa_rele(sr->session, xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
ready = smb_trans_ready(xa);
if (smb_xa_open(xa)) {
smb_xa_rele(sr->session, xa);
smbsr_error(sr, 0, ERRSRV, ERRsrverror);
return (SDRC_ERROR);
}
sr->r_xa = xa;
if (!ready) {
rc = smbsr_encode_empty_result(sr);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
if (!smb_xa_complete(xa)) {
smb_xa_close(xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
return (smb_trans2_dispatch(sr, xa));
}
smb_sdrc_t
smb_pre_transaction2_secondary(smb_request_t *sr)
{
DTRACE_SMB_1(op__Transaction2Secondary__start, smb_request_t *, sr);
return (SDRC_SUCCESS);
}
void
smb_post_transaction2_secondary(smb_request_t *sr)
{
DTRACE_SMB_1(op__Transaction2Secondary__done, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_transaction2_secondary(smb_request_t *sr)
{
uint16_t tpscnt, tdscnt, fid;
uint16_t pscnt, psoff, psdisp, dscnt, dsoff, dsdisp;
smb_xa_t *xa;
int rc;
if ((xa = smbsr_lookup_xa(sr)) == 0) {
smbsr_error(sr, 0, ERRSRV, ERRsrverror);
return (SDRC_ERROR);
}
if (sr->session->signing.flags & SMB_SIGNING_ENABLED) {
if (smb_sign_check_secondary(sr, xa->reply_seqnum) != 0) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
ERRDOS, ERRnoaccess);
return (SDRC_ERROR);
}
}
if (xa->smb_com != SMB_COM_TRANSACTION2) {
return (SDRC_DROP_VC);
}
rc = smbsr_decode_vwv(sr, SMB_TRANS2SHDR_ED_FMT, &tpscnt, &tdscnt,
&pscnt, &psoff, &psdisp, &dscnt, &dsoff, &dsdisp, &fid);
if (rc != 0)
return (SDRC_ERROR);
mutex_enter(&xa->xa_mutex);
if (xa->smb_tpscnt > tpscnt)
xa->smb_tpscnt = tpscnt;
if (xa->smb_tdscnt > tdscnt)
xa->smb_tdscnt = tdscnt;
if (fid != 0xFFFF)
xa->xa_smb_fid = fid;
xa->req_disp_param = psdisp + pscnt;
xa->req_disp_data = dsdisp + dscnt;
/*
* See comment in smb_com_transaction_secondary
*/
if (xa->req_param_mb.chain_offset != psdisp) {
DTRACE_PROBE2(trans_param_disp,
smb_xa_t *, xa, uint16_t, psdisp);
xa->req_param_mb.chain_offset = psdisp;
}
if (xa->req_data_mb.chain_offset != dsdisp) {
DTRACE_PROBE2(trans_data_disp,
smb_xa_t *, xa, uint16_t, dsdisp);
xa->req_data_mb.chain_offset = dsdisp;
}
if (smb_mbc_copy(&xa->req_param_mb, &sr->command, psoff, pscnt)) {
mutex_exit(&xa->xa_mutex);
smb_xa_close(xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
if (smb_mbc_copy(&xa->req_data_mb, &sr->command, dsoff, dscnt)) {
mutex_exit(&xa->xa_mutex);
smb_xa_close(xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
mutex_exit(&xa->xa_mutex);
if (!smb_trans_ready(xa))
return (SDRC_NO_REPLY);
if (!smb_xa_complete(xa))
return (SDRC_NO_REPLY);
return (smb_trans2_dispatch(sr, xa));
}
static smb_sdrc_t
smb_nt_trans_dispatch(struct smb_request *sr, struct smb_xa *xa)
{
int rc;
int total_bytes, n_setup, n_param, n_data;
int param_off, param_pad, data_off, data_pad;
switch (xa->smb_func) {
case NT_TRANSACT_CREATE:
if ((rc = smb_pre_nt_transact_create(sr, xa)) == 0)
rc = smb_nt_transact_create(sr, xa);
smb_post_nt_transact_create(sr, xa);
break;
case NT_TRANSACT_NOTIFY_CHANGE:
rc = smb_nt_transact_notify_change(sr, xa);
break;
case NT_TRANSACT_QUERY_SECURITY_DESC:
rc = smb_nt_transact_query_security_info(sr, xa);
break;
case NT_TRANSACT_SET_SECURITY_DESC:
rc = smb_nt_transact_set_security_info(sr, xa);
break;
case NT_TRANSACT_IOCTL:
rc = smb_nt_transact_ioctl(sr, xa);
break;
case NT_TRANSACT_QUERY_QUOTA:
rc = smb_nt_transact_query_quota(sr, xa);
break;
case NT_TRANSACT_SET_QUOTA:
rc = smb_nt_transact_set_quota(sr, xa);
break;
case NT_TRANSACT_RENAME:
rc = smb_nt_transact_rename(sr, xa);
break;
default:
smbsr_error(sr, 0, ERRSRV, ERRsmbcmd);
return (SDRC_ERROR);
}
switch (rc) {
case SDRC_SUCCESS:
break;
case SDRC_DROP_VC:
case SDRC_NO_REPLY:
case SDRC_ERROR:
case SDRC_SR_KEPT:
return (rc);
case SDRC_NOT_IMPLEMENTED:
smbsr_error(sr, 0, ERRSRV, ERRsmbcmd);
return (SDRC_ERROR);
default:
break;
}
n_setup = MBC_LENGTH(&xa->rep_setup_mb);
n_param = MBC_LENGTH(&xa->rep_param_mb);
n_data = MBC_LENGTH(&xa->rep_data_mb);
if (xa->smb_msrcnt < n_setup ||
xa->smb_mprcnt < n_param ||
xa->smb_mdrcnt < n_data) {
smbsr_error(sr, 0, ERRSRV, ERRsmbcmd);
return (SDRC_ERROR);
}
/* neato, blast it over there */
n_setup = (n_setup + 1) / 2; /* Conver to setup words */
param_pad = 1; /* must be one */
param_off = param_pad + 32 + 37 + (n_setup << 1) + 2;
data_pad = (4 - ((param_off + n_param) & 3)) % 4; /* Pad to 4 byte */
data_off = param_off + n_param + data_pad; /* Param off from hdr */
total_bytes = param_pad + n_param + data_pad + n_data;
rc = smbsr_encode_result(sr, 18+n_setup, total_bytes,
"b3.llllllllbCw#.C#.C",
18 + n_setup, /* wct */
n_param, /* Total Parameter Bytes */
n_data, /* Total Data Bytes */
n_param, /* Total Parameter Bytes this buffer */
param_off, /* Param offset from header start */
0, /* Param displacement */
n_data, /* Total Data Bytes this buffer */
data_off, /* Data offset from header start */
0, /* Data displacement */
n_setup, /* suwcnt */
&xa->rep_setup_mb, /* setup[] */
total_bytes, /* Total data bytes */
param_pad,
&xa->rep_param_mb,
data_pad,
&xa->rep_data_mb);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
smb_sdrc_t
smb_pre_nt_transact(smb_request_t *sr)
{
DTRACE_SMB_1(op__NtTransact__start, smb_request_t *, sr);
return (SDRC_SUCCESS);
}
void
smb_post_nt_transact(smb_request_t *sr)
{
DTRACE_SMB_1(op__NtTransact__done, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_nt_transact(struct smb_request *sr)
{
uint16_t Function;
unsigned char MaxSetupCount, SetupCount;
uint32_t TotalParameterCount, TotalDataCount;
uint32_t MaxParameterCount, MaxDataCount, pscnt;
uint32_t psoff, dscnt, dsoff;
smb_xa_t *xa;
int ready;
int rc;
rc = smbsr_decode_vwv(sr, SMB_NT_TRANSHDR_ED_FMT, &MaxSetupCount,
&TotalParameterCount, &TotalDataCount, &MaxParameterCount,
&MaxDataCount, &pscnt, &psoff, &dscnt,
&dsoff, &SetupCount, &Function);
if (rc != 0)
return (SDRC_ERROR);
xa = smb_xa_create(sr->session, sr, TotalParameterCount, TotalDataCount,
MaxParameterCount, MaxDataCount, MaxSetupCount, SetupCount);
if (xa == 0) {
smbsr_error(sr, 0, ERRSRV, ERRnoroom);
return (SDRC_ERROR);
}
xa->smb_flags = 0;
xa->smb_timeout = 0;
xa->smb_func = Function;
xa->req_disp_param = pscnt;
xa->req_disp_data = dscnt;
if (smb_mbc_copy(&xa->req_setup_mb, &sr->smb_vwv,
sr->smb_vwv.chain_offset, SetupCount * 2)) {
smb_xa_rele(sr->session, xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
if (smb_mbc_copy(&xa->req_param_mb, &sr->command, psoff, pscnt)) {
smb_xa_rele(sr->session, xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
if (smb_mbc_copy(&xa->req_data_mb, &sr->command, dsoff, dscnt)) {
smb_xa_rele(sr->session, xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
ready = smb_trans_ready(xa);
if (smb_xa_open(xa)) {
smb_xa_rele(sr->session, xa);
smbsr_error(sr, 0, ERRSRV, ERRsrverror);
return (SDRC_ERROR);
}
sr->r_xa = xa;
if (!ready) {
rc = smbsr_encode_empty_result(sr);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
if (!smb_xa_complete(xa)) {
smb_xa_close(xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
return (smb_nt_trans_dispatch(sr, xa));
}
smb_sdrc_t
smb_pre_nt_transact_secondary(smb_request_t *sr)
{
DTRACE_SMB_1(op__NtTransactSecondary__start, smb_request_t *, sr);
return (SDRC_SUCCESS);
}
void
smb_post_nt_transact_secondary(smb_request_t *sr)
{
DTRACE_SMB_1(op__NtTransactSecondary__done, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_nt_transact_secondary(struct smb_request *sr)
{
uint16_t tpscnt, tdscnt, fid;
uint16_t pscnt, psoff, psdisp, dscnt, dsoff, dsdisp;
smb_xa_t *xa;
int rc;
if ((xa = smbsr_lookup_xa(sr)) == 0) {
smbsr_error(sr, 0, ERRSRV, ERRsrverror);
return (SDRC_ERROR);
}
if (sr->session->signing.flags & SMB_SIGNING_ENABLED) {
if (smb_sign_check_secondary(sr, xa->reply_seqnum) != 0) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
ERRDOS, ERRnoaccess);
return (SDRC_ERROR);
}
}
if (xa->smb_com != SMB_COM_TRANSACTION2) {
return (SDRC_DROP_VC);
}
rc = smbsr_decode_vwv(sr, SMB_TRANS2SHDR_ED_FMT, &tpscnt, &tdscnt,
&pscnt, &psoff, &psdisp, &dscnt, &dsoff, &dsdisp, &fid);
if (rc != 0)
return (SDRC_ERROR);
mutex_enter(&xa->xa_mutex);
if (xa->smb_tpscnt > tpscnt)
xa->smb_tpscnt = tpscnt;
if (xa->smb_tdscnt > tdscnt)
xa->smb_tdscnt = tdscnt;
if (fid != 0xFFFF)
xa->xa_smb_fid = fid;
xa->req_disp_param = psdisp + pscnt;
xa->req_disp_data = dsdisp + dscnt;
/*
* See comment in smb_com_transaction_secondary
*/
if (xa->req_param_mb.chain_offset != psdisp) {
DTRACE_PROBE2(trans_param_disp,
smb_xa_t *, xa, uint16_t, psdisp);
xa->req_param_mb.chain_offset = psdisp;
}
if (xa->req_data_mb.chain_offset != dsdisp) {
DTRACE_PROBE2(trans_data_disp,
smb_xa_t *, xa, uint16_t, dsdisp);
xa->req_data_mb.chain_offset = dsdisp;
}
if (smb_mbc_copy(&xa->req_param_mb, &sr->command, psoff, pscnt)) {
mutex_exit(&xa->xa_mutex);
smb_xa_close(xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
if (smb_mbc_copy(&xa->req_data_mb, &sr->command, dsoff, dscnt)) {
mutex_exit(&xa->xa_mutex);
smb_xa_close(xa);
smbsr_error(sr, 0, ERRDOS, ERRbadformat);
return (SDRC_ERROR);
}
mutex_exit(&xa->xa_mutex);
if (!smb_trans_ready(xa))
return (SDRC_NO_REPLY);
if (!smb_xa_complete(xa))
return (SDRC_NO_REPLY);
return (smb_nt_trans_dispatch(sr, xa));
}
static int
smb_trans_ready(smb_xa_t *xa)
{
int rc;
mutex_enter(&xa->xa_mutex);
rc = xa->req_disp_data >= xa->smb_tdscnt &&
xa->req_disp_param >= xa->smb_tpscnt;
mutex_exit(&xa->xa_mutex);
return (rc);
}
static void
smb_encode_SHARE_INFO_1(struct mbuf_chain *output, struct mbuf_chain *text,
char *oem_name, uint16_t type, char *comment)
{
(void) smb_mbc_encodef(output, "13c.wl", oem_name,
type, MBC_LENGTH(text));
(void) smb_mbc_encodef(text, "s", comment ? comment : "");
}
static void
smb_encode_SHARE_INFO_2(struct mbuf_chain *output, struct mbuf_chain *text,
smb_request_t *sr, char *oem_name, uint16_t type,
char *comment, uint16_t access, char *path, char *password)
{
unsigned char pword[9];
bzero(pword, sizeof (pword));
(void) strncpy((char *)pword, password, sizeof (pword));
smb_encode_SHARE_INFO_1(output, text, oem_name, type, comment);
(void) smb_mbc_encodef(output, "wwwl9c.",
access,
sr->sr_cfg->skc_maxconnections,
smb_server_get_session_count(sr->sr_server),
MBC_LENGTH(text),
pword);
(void) smb_mbc_encodef(text, "s", path);
}
int
smb_trans_net_share_enum(struct smb_request *sr, struct smb_xa *xa)
{
uint16_t pid_hi, pid_lo;
/*
* Number of data bytes that will
* be sent in the current response
*/
uint16_t data_scnt;
/*
* Total number of data bytes that
* are sent till now. This is only
* used for calculating current data
* displacement
*/
uint16_t tot_data_scnt;
/*
* Number of parameter bytes should
* be sent for the current response.
* It is 8 for the 1st response and
* 0 for others
*/
uint16_t param_scnt;
/* number of setup and parameter bytes */
uint16_t n_setup, n_param;
/* data and parameter displacement */
uint16_t data_disp, param_disp;
/* parameter and data offset and pad */
int param_off, param_pad, data_off, data_pad;
/*
* total bytes of parameters and data
* in the packet, plus the pad bytes.
*/
int tot_packet_bytes;
boolean_t first_resp;
char fmt[16];
struct mbuf_chain reply;
uint16_t level;
uint16_t pkt_bufsize;
smb_enumshare_info_t esi;
char *sent_buf;
ASSERT(sr->uid_user);
if (smb_mbc_decodef(&xa->req_param_mb, "ww", &level,
&esi.es_bufsize) != 0)
return (SDRC_NOT_IMPLEMENTED);
if (level != 1) {
/*
* Only level 1 is valid for NetShareEnum
* None of the error codes in the spec are meaningful
* here. This error code is returned by Windows.
*/
(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww",
ERROR_INVALID_LEVEL, 0, 0, 0);
return (SDRC_SUCCESS);
}
esi.es_buf = smb_srm_zalloc(sr, esi.es_bufsize);
esi.es_posix_uid = crgetuid(sr->uid_user->u_cred);
smb_kshare_enum(sr->sr_server, &esi);
/* client buffer size is not big enough to hold any shares */
if (esi.es_nsent == 0) {
(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww",
ERROR_MORE_DATA, 0, esi.es_nsent, esi.es_ntotal);
return (SDRC_SUCCESS);
}
/*
* Initialize the reply mbuf chain. Note that we re-initialize
* this on each pass through the loop below.
*/
MBC_SETUP(&reply, smb_maxbufsize);
/*
* The rep_setup_mb is already initialized in smb_trans_dispatch().
* Calling MBC_INIT() will initialized the structure and so the
* pointer to the mbuf chains will be lost. Therefore, we need
* to free the resources before calling MBC_INIT() again.
*/
n_setup = 0; /* Setup count for NetShareEnum SMB is 0 */
MBC_FLUSH(&xa->rep_setup_mb);
n_param = 8;
pkt_bufsize = sr->session->smb_msg_size -
(SMB_HEADER_ED_LEN + RESP_HEADER_LEN + n_param);
tot_data_scnt = 0;
sent_buf = esi.es_buf;
first_resp = B_TRUE;
while (tot_data_scnt < esi.es_datasize) {
data_scnt = esi.es_datasize - tot_data_scnt;
if (data_scnt > pkt_bufsize)
data_scnt = pkt_bufsize;
MBC_FLUSH(&xa->rep_data_mb);
(void) sprintf(fmt, "%dc", data_scnt);
(void) smb_mbc_encodef(&xa->rep_data_mb, fmt, sent_buf);
sent_buf += data_scnt;
tot_data_scnt += data_scnt;
/* Only the 1st response packet contains parameters */
param_scnt = (first_resp) ? n_param : 0;
param_pad = 1; /* always one */
param_off = SMB_HEADER_ED_LEN + RESP_HEADER_LEN;
param_disp = (first_resp) ? 0 : n_param;
MBC_FLUSH(&xa->rep_param_mb);
if (first_resp) {
first_resp = B_FALSE;
(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww",
(esi.es_ntotal > esi.es_nsent)
? ERROR_MORE_DATA : 0,
0, esi.es_nsent, esi.es_ntotal);
}
data_pad = (param_off + n_param) & 1; /* Pad to short */
/* data off from hdr start */
data_off = param_off + param_scnt + data_pad;
data_disp = tot_data_scnt - data_scnt;
tot_packet_bytes = param_pad + param_scnt + data_pad +
data_scnt;
pid_hi = sr->smb_pid >> 16;
pid_lo = (uint16_t)sr->smb_pid;
MBC_FLUSH(&reply);
(void) smb_mbc_encodef(&reply, SMB_HEADER_ED_FMT,
sr->first_smb_com,
sr->smb_rcls,
sr->smb_reh,
sr->smb_err,
sr->smb_flg | SMB_FLAGS_REPLY,
sr->smb_flg2,
pid_hi,
sr->smb_sig,
sr->smb_tid,
pid_lo,
sr->smb_uid,
sr->smb_mid);
(void) smb_mbc_encodef(&reply,
"bww2.wwwwwwb.Cw#.C#.C",
10 + n_setup, /* wct */
n_param, /* Total Parameter Bytes */
esi.es_datasize, /* Total Data Bytes */
param_scnt, /* Total Parameter Bytes this buffer */
param_off, /* Param offset from header start */
param_disp, /* Param displacement */
data_scnt, /* Total Data Bytes this buffer */
data_off, /* Data offset from header start */
data_disp, /* Data displacement */
n_setup, /* suwcnt */
&xa->rep_setup_mb, /* setup[] */
tot_packet_bytes, /* Total data bytes */
param_pad,
&xa->rep_param_mb,
data_pad,
&xa->rep_data_mb);
if (sr->session->signing.flags & SMB_SIGNING_ENABLED)
smb_sign_reply(sr, &reply);
(void) smb_session_send(sr->session, 0, &reply);
}
m_freem(reply.chain);
return (SDRC_NO_REPLY);
}
int
smb_trans_net_share_getinfo(smb_request_t *sr, struct smb_xa *xa)
{
uint16_t level, max_bytes, access;
struct mbuf_chain str_mb;
char *share;
char *password;
smb_kshare_t *si;
if (smb_mbc_decodef(&xa->req_param_mb, "%sww", sr,
&share, &level, &max_bytes) != 0)
return (SDRC_NOT_IMPLEMENTED);
si = smb_kshare_lookup(sr->sr_server, share);
if ((si == NULL) || (si->shr_oemname == NULL)) {
(void) smb_mbc_encodef(&xa->rep_param_mb, "www",
NERR_NetNameNotFound, 0, 0);
if (si)
smb_kshare_release(sr->sr_server, si);
return (SDRC_SUCCESS);
}
access = SHARE_ACCESS_ALL;
password = "";
MBC_INIT(&str_mb, max_bytes);
switch (level) {
case 0 :
(void) smb_mbc_encodef(&xa->rep_data_mb, "13c",
si->shr_oemname);
break;
case 1 :
smb_encode_SHARE_INFO_1(&xa->rep_data_mb, &str_mb,
si->shr_oemname, si->shr_type, si->shr_cmnt);
break;
case 2 :
smb_encode_SHARE_INFO_2(&xa->rep_data_mb, &str_mb, sr,
si->shr_oemname, si->shr_type, si->shr_cmnt, access,
si->shr_path, password);
break;
default:
smb_kshare_release(sr->sr_server, si);
(void) smb_mbc_encodef(&xa->rep_param_mb, "www",
ERROR_INVALID_LEVEL, 0, 0);
m_freem(str_mb.chain);
return (SDRC_NOT_IMPLEMENTED);
}
smb_kshare_release(sr->sr_server, si);
(void) smb_mbc_encodef(&xa->rep_param_mb, "www", NERR_Success,
-MBC_LENGTH(&xa->rep_data_mb),
MBC_LENGTH(&xa->rep_data_mb) + MBC_LENGTH(&str_mb));
(void) smb_mbc_encodef(&xa->rep_data_mb, "C", &str_mb);
m_freem(str_mb.chain);
return (SDRC_SUCCESS);
}
int
smb_trans_net_workstation_getinfo(struct smb_request *sr, struct smb_xa *xa)
{
uint16_t level, max_bytes;
struct mbuf_chain str_mb;
char *domain;
char *hostname;
if ((smb_mbc_decodef(&xa->req_param_mb, "ww",
&level, &max_bytes) != 0) ||
(level != 10)) {
(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww",
NERR_BadTransactConfig, 0, 0, 0);
return (SDRC_SUCCESS);
}
domain = sr->sr_cfg->skc_nbdomain;
hostname = sr->sr_cfg->skc_hostname;
MBC_INIT(&str_mb, max_bytes);
(void) smb_mbc_encodef(&str_mb, "."); /* Prevent NULL pointers */
(void) smb_mbc_encodef(&xa->rep_data_mb, "l", MBC_LENGTH(&str_mb));
(void) smb_mbc_encodef(&str_mb, "s", hostname);
(void) smb_mbc_encodef(&xa->rep_data_mb, "l", MBC_LENGTH(&str_mb));
(void) smb_mbc_encodef(&str_mb, "s", "nobody");
(void) smb_mbc_encodef(&xa->rep_data_mb, "l", MBC_LENGTH(&str_mb));
(void) smb_mbc_encodef(&str_mb, "s", domain);
(void) smb_mbc_encodef(&xa->rep_data_mb, "bbl",
(uint8_t)sr->sr_cfg->skc_version.sv_major,
(uint8_t)sr->sr_cfg->skc_version.sv_minor,
MBC_LENGTH(&str_mb));
(void) smb_mbc_encodef(&str_mb, "s", domain);
(void) smb_mbc_encodef(&xa->rep_data_mb, "l", MBC_LENGTH(&str_mb));
(void) smb_mbc_encodef(&str_mb, "s", domain);
(void) smb_mbc_encodef(&xa->rep_param_mb, "www", 0,
-MBC_LENGTH(&xa->rep_data_mb),
MBC_LENGTH(&xa->rep_data_mb) + MBC_LENGTH(&str_mb));
(void) smb_mbc_encodef(&xa->rep_data_mb, "C", &str_mb);
m_freem(str_mb.chain);
return (SDRC_SUCCESS);
}
int
smb_trans_net_user_getinfo(struct smb_request *sr, struct smb_xa *xa)
{
uint16_t level, max_bytes;
unsigned char *user;
int rc;
rc = smb_mbc_decodef(&xa->req_param_mb, "%sww", sr,
&user,
&level,
&max_bytes);
if (rc != 0)
return (SDRC_NOT_IMPLEMENTED);
(void) smb_mbc_encodef(&xa->rep_param_mb, "www",
NERR_UserNotFound, 0, 0);
return (SDRC_SUCCESS);
}
smb_sdrc_t
smb_trans_net_server_getinfo(struct smb_request *sr, struct smb_xa *xa)
{
uint16_t level, buf_size;
uint16_t avail_data, max_data;
char server_name[16];
struct mbuf_chain str_mb;
if (smb_mbc_decodef(&xa->req_param_mb, "ww", &level, &buf_size) != 0)
return (SDRC_ERROR);
max_data = MBC_MAXBYTES(&xa->rep_data_mb);
MBC_INIT(&str_mb, buf_size);
bzero(server_name, sizeof (server_name));
(void) strncpy(server_name, sr->sr_cfg->skc_hostname,
sizeof (server_name));
/* valid levels are 0 and 1 */
switch (level) {
case 0:
(void) smb_mbc_encodef(&xa->rep_data_mb, "16c", server_name);
break;
case 1:
(void) smb_mbc_encodef(&str_mb, "s",
sr->sr_cfg->skc_system_comment);
(void) smb_mbc_encodef(&xa->rep_data_mb, "16cbbll", server_name,
(uint8_t)sr->sr_cfg->skc_version.sv_major,
(uint8_t)sr->sr_cfg->skc_version.sv_minor,
MY_SERVER_TYPE, max_data - MBC_LENGTH(&str_mb));
break;
default:
(void) smb_mbc_encodef(&xa->rep_param_mb, "www",
ERROR_INVALID_LEVEL, 0, 0);
m_freem(str_mb.chain);
return (SDRC_SUCCESS);
}
avail_data = MBC_LENGTH(&xa->rep_data_mb) + MBC_LENGTH(&str_mb);
(void) smb_mbc_encodef(&xa->rep_param_mb, "www",
NERR_Success, max_data - avail_data, avail_data);
(void) smb_mbc_encodef(&xa->rep_data_mb, "C", &str_mb);
m_freem(str_mb.chain);
return (SDRC_SUCCESS);
}
/*
* 6.4 The NetServerEnum2 RAP Service
*
* The NetServerEnum2 RAP service lists all computers of the specified type
* or types that are visible in the specified domains. It may also
* enumerate domains.
*
* The following definition uses the notation and terminology defined in
* the CIFS Remote Administration Protocol specification, which is required
* in order to make it well-defined. The definition is:
*
* uint16_t NetServerEnum2 (
* uint16_t sLevel,
* RCVBUF pbBuffer,
* RCVBUFLEN cbBuffer,
* ENTCOUNT pcEntriesRead,
* uint16_t *pcTotalAvail,
* uint32_t fServerType,
* char *pszDomain,
* );
*
* where:
*
* sLevel specifies the level of detail (0 or 1) requested.
*
* pbBuffer points to the buffer to receive the returned data. If the
* function is successful, the buffer contains a sequence of
* server_info_x structures, where x is 0 or 1, depending on the
* level of detail requested.
*
* cbBuffer specifies the size, in bytes, of the buffer pointed to by
* the pbBuffer parameter.
*
* pcEntriesRead points to a 16 bit variable that receives a count of
* the number of servers enumerated in the buffer. This count is
* valid only if NetServerEnum2 returns the NERR_Success or
* ERROR_MORE_DATA values.
*
* pcTotal Avail points to a 16 bit variable that receives a count of
* the total number of available entries. This count is valid only if
* NetServerEnum2 returns the NERR_Success or ERROR_MORE_DATA values.
*
* fServerType specifies the type or types of computers to enumerate.
* Computers that match at least one of the specified types are
* returned in the buffer. Possible values are defined in the request
* parameters section.
*
* pszDomain points to a null-terminated string that contains the
* name of the workgroup in which to enumerate computers of the
* specified type or types. If the pszDomain parameter is a null
* string or a null pointer, servers are enumerated for the current
* domain of the computer.
*
* 6.4.1 Transaction Request Parameters section
*
* The Transaction request parameters section in this instance contains:
* . The 16 bit function number for NetServerEnum2 which is 104.
* . The parameter descriptor string which is "WrLehDz".
* . The data descriptor string for the (returned) data which is "B16" for
* level detail 0 or "B16BBDz" for level detail 1.
* . The actual parameters as described by the parameter descriptor
* string.
*
* The parameters are:
* . A 16 bit integer with a value of 0 or 1 (corresponding to the "W" in
* the parameter descriptor string. This represents the level of detail
* the server is expected to return
* . A 16 bit integer that contains the size of the receive buffer.
* . A 32 bit integer that represents the type of servers the function
* should enumerate. The possible values may be any of the following or
* a combination of the following:
*
* SV_TYPE_WORKSTATION 0x00000001 All workstations
* SV_TYPE_SERVER 0x00000002 All servers
* SV_TYPE_SQLSERVER 0x00000004 Any server running with SQL
* server
* SV_TYPE_DOMAIN_CTRL 0x00000008 Primary domain controller
* SV_TYPE_DOMAIN_BAKCTRL 0x00000010 Backup domain controller
* SV_TYPE_TIME_SOURCE 0x00000020 Server running the timesource
* service
* SV_TYPE_AFP 0x00000040 Apple File Protocol servers
* SV_TYPE_NOVELL 0x00000080 Novell servers
* SV_TYPE_DOMAIN_MEMBER 0x00000100 Domain Member
* SV_TYPE_PRINTQ_SERVER 0x00000200 Server sharing print queue
* SV_TYPE_DIALIN_SERVER 0x00000400 Server running dialin service.
* SV_TYPE_XENIX_SERVER 0x00000800 Xenix server
* SV_TYPE_NT 0x00001000 NT server
* SV_TYPE_WFW 0x00002000 Server running Windows for
* Workgroups
* SV_TYPE_SERVER_NT 0x00008000 Windows NT non DC server
* SV_TYPE_POTENTIAL_BROWSER 0x00010000 Server that can run the browser
* service
* SV_TYPE_BACKUP_BROWSER 0x00020000 Backup browser server
* SV_TYPE_MASTER_BROWSER 0x00040000 Master browser server
* SV_TYPE_DOMAIN_MASTER 0x00080000 Domain Master Browser server
* SV_TYPE_LOCAL_LIST_ONLY 0x40000000 Enumerate only entries marked
* "local"
* SV_TYPE_DOMAIN_ENUM 0x80000000 Enumerate Domains. The pszDomain
* parameter must be NULL.
*
* . A null terminated ASCII string representing the pszDomain parameter
* described above
*
* 6.4.2 Transaction Request Data section
*
* There is no data or auxiliary data to send as part of the request.
*
* 6.4.3 Transaction Response Parameters section
*
* The transaction response parameters section consists of:
* . A 16 bit word indicating the return status. The possible values are:
*
* Code Value Description
* NERR_Success 0 No errors encountered
* ERROR_MORE_DATA 234 Additional data is available
* NERR_ServerNotStarted 2114 The RAP service on the remote computer
* is not running
* NERR_BadTransactConfig 2141 The server is not configured for
* transactions, IPC$ is not shared
*
* . A 16 bit "converter" word.
* . A 16 bit number representing the number of entries returned.
* . A 16 bit number representing the total number of available entries.
* If the supplied buffer is large enough, this will equal the number of
* entries returned.
*
* 6.4.4 Transaction Response Data section
*
* The return data section consists of a number of SERVER_INFO_1 structures.
* The number of such structures present is determined by the third entry
* (described above) in the return parameters section.
*
* At level detail 0, the Transaction response data section contains a
* number of SERVER_INFO_0 data structure. The number of such structures is
* equal to the 16 bit number returned by the server in the third parameter
* in the Transaction response parameter section. The SERVER_INFO_0 data
* structure is defined as:
*
* struct SERVER_INFO_0 {
* char sv0_name[16];
* };
*
* where:
*
* sv0_name is a null-terminated string that specifies the name of a
* computer or domain .
*
* At level detail 1, the Transaction response data section contains a
* number of SERVER_INFO_1 data structure. The number of such structures is
* equal to the 16 bit number returned by the server in the third parameter
* in the Transaction response parameter section. The SERVER_INFO_1 data
* structure is defined as:
*
* struct SERVER_INFO_1 {
* char sv1_name[16];
* char sv1_version_major;
* char sv1_version_minor;
* uint32_t sv1_type;
* char *sv1_comment_or_master_browser;
* };
*
* sv1_name contains a null-terminated string that specifies the name
* of a computer, or a domain name if SV_TYPE_DOMAIN_ENUM is set in
* sv1_type.
*
* sv1_version_major whatever was specified in the HostAnnouncement
* or DomainAnnouncement frame with which the entry was registered.
*
* sv1_version_minor whatever was specified in the HostAnnouncement
* or DomainAnnouncement frame with which the entry was registered.
*
* sv1_type specifies the type of software the computer is running.
* The member can be one or a combination of the values defined above
* in the Transaction request parameters section for fServerType.
*
*
* sv1_comment_or_master_browser points to a null-terminated string. If
* the sv1_type indicates that the entry is for a domain, this
* specifies the name of server running the domain master browser;
* otherwise, it specifies a comment describing the server. The comment
* can be a null string or the pointer may be a null pointer.
*
* In case there are multiple SERVER_INFO_1 data structures to
* return, the server may put all these fixed length structures in
* the return buffer, leave some space and then put all the variable
* length data (the actual value of the sv1_comment strings) at the
* end of the buffer.
*
* There is no auxiliary data to receive.
*/
int
smb_trans_net_server_enum2(struct smb_request *sr, struct smb_xa *xa)
{
uint16_t opcode, level, max_bytes;
uint32_t server_type;
unsigned char *domain;
struct mbuf_chain str_mb;
char *hostname, *s;
smb_kmod_cfg_t *si;
if (smb_mbc_decodef(&xa->req_param_mb,
"%wsswwls", sr, &opcode, &s, &s,
&level, &max_bytes, &server_type, &domain) != 0)
return (SDRC_NOT_IMPLEMENTED);
si = sr->sr_cfg;
if (smb_strcasecmp(si->skc_nbdomain, (char *)domain, 0) != 0) {
(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww", 0, 0, 0, 0);
return (SDRC_SUCCESS);
}
if ((server_type & MY_SERVER_TYPE) == 0) {
(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww", 0, 0, 0, 0);
return (SDRC_SUCCESS);
}
MBC_INIT(&str_mb, max_bytes);
hostname = si->skc_hostname;
(void) smb_mbc_encodef(&xa->rep_data_mb, "16c", hostname);
if (level == 1) {
(void) smb_mbc_encodef(&xa->rep_data_mb, "bbll",
(uint8_t)sr->sr_cfg->skc_version.sv_major,
(uint8_t)sr->sr_cfg->skc_version.sv_minor,
MY_SERVER_TYPE, MBC_LENGTH(&str_mb));
(void) smb_mbc_encodef(&str_mb, "s", si->skc_system_comment);
}
(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww", 0,
-MBC_LENGTH(&xa->rep_data_mb), 1, 1);
(void) smb_mbc_encodef(&xa->rep_data_mb, "m", str_mb.chain);
return (SDRC_SUCCESS);
}
static boolean_t
is_supported_mailslot(const char *mailslot)
{
static char *mailslots[] = {
PIPE_LANMAN,
MAILSLOT_LANMAN,
MAILSLOT_BROWSE,
MAILSLOT_MSBROWSE
};
int i;
for (i = 0; i < sizeof (mailslots)/sizeof (mailslots[0]); ++i)
if (smb_strcasecmp(mailslot, mailslots[i], 0) == 0)
return (B_TRUE);
return (B_FALSE);
}
/*
* smb_trans_nmpipe
*
* This is used for RPC bind and request transactions.
*
* If the data available from the pipe is larger than the maximum
* data size requested by the client, return as much as requested.
* The residual data remains in the pipe until the client comes back
* with a read request or closes the pipe.
*
* When we read less than what's available, we MUST return the
* status NT_STATUS_BUFFER_OVERFLOW (or ERRDOS/ERROR_MORE_DATA)
*/
static smb_sdrc_t
smb_trans_nmpipe(smb_request_t *sr, smb_xa_t *xa)
{
smb_fsctl_t fsctl;
uint32_t status;
smbsr_lookup_file(sr);
if (sr->fid_ofile == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
ERRDOS, ERRbadfid);
return (SDRC_ERROR);
}
/*
* A little confusing perhaps, but the fsctl "input" is what we
* write to the pipe (from the transaction "send" data), and the
* fsctl "output" is what we read from the pipe (and becomes the
* transaction receive data).
*/
fsctl.CtlCode = FSCTL_PIPE_TRANSCEIVE;
fsctl.InputCount = xa->smb_tdscnt; /* write count */
fsctl.OutputCount = 0; /* minimum to read from the pipe */
fsctl.MaxOutputResp = xa->smb_mdrcnt; /* max to read */
fsctl.in_mbc = &xa->req_data_mb; /* write from here */
fsctl.out_mbc = &xa->rep_data_mb; /* read into here */
status = smb_opipe_fsctl(sr, &fsctl);
if (status) {
smbsr_status(sr, status, 0, 0);
if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_ERROR)
return (SDRC_ERROR);
/* Warnings like NT_STATUS_BUFFER_OVERFLOW are OK */
}
return (SDRC_SUCCESS);
}
static smb_sdrc_t
smb_trans_dispatch(smb_request_t *sr, smb_xa_t *xa)
{
int rc, pos;
int total_bytes, n_setup, n_param, n_data;
int param_off, param_pad, data_off, data_pad;
uint16_t opcode;
uint16_t devstate;
char *req_fmt;
char *rep_fmt;
if (xa->smb_suwcnt > 0) {
rc = smb_mbc_decodef(&xa->req_setup_mb, "ww", &opcode,
&sr->smb_fid);
if (rc != 0)
goto trans_err_not_supported;
switch (opcode) {
case TRANS_SET_NMPIPE_STATE:
if ((rc = smb_mbc_decodef(&xa->req_param_mb, "w",
&devstate)) != 0)
goto trans_err_not_supported;
rc = SDRC_SUCCESS;
break;
case TRANS_TRANSACT_NMPIPE:
rc = smb_trans_nmpipe(sr, xa);
break;
case TRANS_WAIT_NMPIPE:
delay(SEC_TO_TICK(1));
rc = SDRC_SUCCESS;
break;
default:
goto trans_err_not_supported;
}
} else {
if (!is_supported_mailslot(xa->xa_pipe_name))
goto trans_err_not_supported;
if ((rc = smb_mbc_decodef(&xa->req_param_mb, "%wss", sr,
&opcode, &req_fmt, &rep_fmt)) != 0)
goto trans_err_not_supported;
switch (opcode) {
case API_WshareEnum:
rc = smb_trans_net_share_enum(sr, xa);
break;
case API_WshareGetInfo:
rc = smb_trans_net_share_getinfo(sr, xa);
break;
case API_WserverGetInfo:
rc = smb_trans_net_server_getinfo(sr, xa);
break;
case API_WUserGetInfo:
rc = smb_trans_net_user_getinfo(sr, xa);
break;
case API_WWkstaGetInfo:
rc = smb_trans_net_workstation_getinfo(sr, xa);
break;
case API_NetServerEnum2:
rc = smb_trans_net_server_enum2(sr, xa);
break;
default:
goto trans_err_not_supported;
}
}
switch (rc) {
case SDRC_SUCCESS:
break;
case SDRC_DROP_VC:
case SDRC_NO_REPLY:
case SDRC_ERROR:
return (rc);
case SDRC_NOT_IMPLEMENTED:
goto trans_err_not_supported;
default:
break;
}
n_setup = MBC_LENGTH(&xa->rep_setup_mb);
n_param = MBC_LENGTH(&xa->rep_param_mb);
n_data = MBC_LENGTH(&xa->rep_data_mb);
if (xa->smb_msrcnt < n_setup ||
xa->smb_mprcnt < n_param ||
xa->smb_mdrcnt < n_data) {
goto trans_err_too_small;
}
/* neato, blast it over there */
n_setup = (n_setup + 1) / 2; /* Convert to setup words */
param_pad = 1; /* always one */
param_off = param_pad + 32 + 21 + (n_setup << 1) + 2;
data_pad = (param_off + n_param) & 1; /* Pad to short */
/* Param off from hdr start */
data_off = param_off + n_param + data_pad;
total_bytes = param_pad + n_param + data_pad + n_data;
rc = smbsr_encode_result(sr, 10+n_setup, total_bytes,
"bww2.wwwwwwb.Cw#.C#.C",
10 + n_setup, /* wct */
n_param, /* Total Parameter Bytes */
n_data, /* Total Data Bytes */
n_param, /* Total Parameter Bytes this buffer */
param_off, /* Param offset from header start */
0, /* Param displacement */
n_data, /* Total Data Bytes this buffer */
data_off, /* Data offset from header start */
0, /* Data displacement */
n_setup, /* suwcnt */
&xa->rep_setup_mb, /* setup[] */
total_bytes, /* Total data bytes */
param_pad,
&xa->rep_param_mb,
data_pad,
&xa->rep_data_mb);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
trans_err_too_small:
rc = NERR_BufTooSmall;
goto trans_err;
trans_err_not_supported:
rc = ERROR_NOT_SUPPORTED;
goto trans_err;
trans_err:
pos = MBC_LENGTH(&sr->reply) + 23;
rc = smbsr_encode_result(sr, 10, 4, "bww2.wwwwwwb.www",
10, /* wct */
4, 0, /* tpscnt tdscnt */
4, pos, 0, /* pscnt psoff psdisp */
0, 0, 0, /* dscnt dsoff dsdisp */
0, /* suwcnt */
4, /* bcc */
rc,
0); /* converter word? */
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
static smb_sdrc_t
smb_trans2_dispatch(smb_request_t *sr, smb_xa_t *xa)
{
int rc, pos;
int total_bytes, n_setup, n_param, n_data;
int param_off, param_pad, data_off;
uint16_t data_pad;
uint16_t opcode;
uint16_t nt_unknown_secret = 0x0100;
char *fmt;
n_data = xa->smb_mdrcnt;
if (smb_mbc_decodef(&xa->req_setup_mb, "w", &opcode) != 0)
goto trans_err_not_supported;
/*
* Save this for /proc to read later.
*/
xa->smb_func = opcode;
/* for now, only respond to the */
switch (opcode) {
case TRANS2_OPEN2:
rc = smb_com_trans2_open2(sr, xa);
break;
case TRANS2_CREATE_DIRECTORY:
rc = smb_com_trans2_create_directory(sr, xa);
break;
case TRANS2_FIND_FIRST2:
/*
* Should have enough room to send the response
* data back to client.
*/
if (n_data == 0) {
smbsr_error(sr, NT_STATUS_INFO_LENGTH_MISMATCH,
ERRDOS, ERROR_BAD_LENGTH);
return (SDRC_ERROR);
}
rc = smb_com_trans2_find_first2(sr, xa);
break;
case TRANS2_FIND_NEXT2:
/*
* Should have enough room to send the response
* data back to client.
*/
if (n_data == 0) {
smbsr_error(sr, NT_STATUS_INFO_LENGTH_MISMATCH,
ERRDOS, ERROR_BAD_LENGTH);
return (SDRC_ERROR);
}
rc = smb_com_trans2_find_next2(sr, xa);
break;
case TRANS2_QUERY_FS_INFORMATION:
/*
* Should have enough room to send the response
* data back to client.
*/
if (n_data == 0) {
smbsr_error(sr, NT_STATUS_INFO_LENGTH_MISMATCH,
ERRDOS, ERROR_BAD_LENGTH);
return (SDRC_ERROR);
}
rc = smb_com_trans2_query_fs_information(sr, xa);
break;
case TRANS2_SET_FS_INFORMATION:
rc = smb_com_trans2_set_fs_information(sr, xa);
break;
case TRANS2_QUERY_PATH_INFORMATION:
/*
* Should have enough room to send the response
* data back to client.
*/
if (n_data == 0) {
smbsr_error(sr, NT_STATUS_INFO_LENGTH_MISMATCH,
ERRDOS, ERROR_BAD_LENGTH);
return (SDRC_ERROR);
}
rc = smb_com_trans2_query_path_information(sr, xa);
break;
case TRANS2_QUERY_FILE_INFORMATION:
/*
* Should have enough room to send the response
* data back to client.
*/
if (n_data == 0) {
smbsr_error(sr, NT_STATUS_INFO_LENGTH_MISMATCH,
ERRDOS, ERROR_BAD_LENGTH);
return (SDRC_ERROR);
}
rc = smb_com_trans2_query_file_information(sr, xa);
break;
case TRANS2_SET_PATH_INFORMATION:
rc = smb_com_trans2_set_path_information(sr, xa);
break;
case TRANS2_SET_FILE_INFORMATION:
rc = smb_com_trans2_set_file_information(sr, xa);
break;
case TRANS2_GET_DFS_REFERRAL:
rc = smb_com_trans2_get_dfs_referral(sr, xa);
break;
default:
(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
goto trans_err_not_supported;
}
switch (rc) {
case SDRC_SUCCESS:
break;
case SDRC_DROP_VC:
case SDRC_NO_REPLY:
case SDRC_ERROR:
return (rc);
case SDRC_NOT_IMPLEMENTED:
goto trans_err_not_supported;
default:
break;
}
n_setup = MBC_LENGTH(&xa->rep_setup_mb);
n_param = MBC_LENGTH(&xa->rep_param_mb);
n_data = MBC_LENGTH(&xa->rep_data_mb);
if (xa->smb_msrcnt < n_setup ||
xa->smb_mprcnt < n_param ||
xa->smb_mdrcnt < n_data) {
goto trans_err_too_small;
}
/* neato, blast it over there */
n_setup = (n_setup + 1) / 2; /* Conver to setup words */
param_pad = 1; /* must be one */
param_off = param_pad + 32 + 21 + (n_setup << 1) + 2;
/*
* Including the nt_unknown_secret value persuades netmon to
* display the correct data format for QueryPathInfo and
* QueryFileInfo.
*/
if (opcode == TRANS2_QUERY_FILE_INFORMATION ||
opcode == TRANS2_QUERY_PATH_INFORMATION) {
data_pad = sizeof (uint16_t);
data_off = param_off + n_param + data_pad;
fmt = "bww2.wwwwwwb.Cw#.CwC";
nt_unknown_secret = 0x0100;
}
else
{
data_pad = (param_off + n_param) & 1; /* Pad to short */
/* Param off from hdr start */
data_off = param_off + n_param + data_pad;
fmt = "bww2.wwwwwwb.Cw#.C#.C";
nt_unknown_secret = data_pad;
}
total_bytes = param_pad + n_param + data_pad + n_data;
rc = smbsr_encode_result(sr, 10+n_setup, total_bytes,
fmt,
10 + n_setup, /* wct */
n_param, /* Total Parameter Bytes */
n_data /* + data_pad */, /* Total Data Bytes */
n_param, /* Total Parameter Bytes this buffer */
param_off, /* Param offset from header start */
0, /* Param displacement */
n_data /* + data_pad */, /* Total Data Bytes this buffer */
data_off, /* Data offset from header start */
0, /* Data displacement */
n_setup, /* suwcnt */
&xa->rep_setup_mb, /* setup[] */
total_bytes, /* Total data bytes */
param_pad,
&xa->rep_param_mb,
nt_unknown_secret,
&xa->rep_data_mb);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
trans_err_too_small:
rc = NERR_BufTooSmall;
goto trans_err;
trans_err_not_supported:
rc = ERROR_NOT_SUPPORTED;
goto trans_err;
trans_err:
pos = MBC_LENGTH(&sr->reply) + 23;
rc = smbsr_encode_result(sr, 10, 4, "bww2.wwwwwwb.www",
10, /* wct */
4, 0, /* tpscnt tdscnt */
4, pos, 0, /* pscnt psoff psdisp */
0, 0, 0, /* dscnt dsoff dsdisp */
0, /* suwcnt */
4, /* bcc */
rc,
0); /* converter word? */
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
static uint32_t smb_xa_max_setup_count = 200;
static uint32_t smb_xa_max_param_count = 32 * 1024;
static uint32_t smb_xa_max_data_count = 64 * 1024;
smb_xa_t *
smb_xa_create(
smb_session_t *session,
smb_request_t *sr,
uint32_t total_parameter_count,
uint32_t total_data_count,
uint32_t max_parameter_count,
uint32_t max_data_count,
uint32_t max_setup_count,
uint32_t setup_word_count)
{
smb_xa_t *xa, *nxa;
smb_llist_t *xlist;
/*
* Sanity check what the client says it will send.
* Caller handles NULL return as ERRnoroom.
*/
if (setup_word_count > smb_xa_max_setup_count)
return (NULL);
if (total_parameter_count > smb_xa_max_param_count)
return (NULL);
if (total_data_count > smb_xa_max_data_count)
return (NULL);
/*
* Limit what the client asks us to allocate for
* returned setup, params, data.
*/
if (max_setup_count > smb_xa_max_setup_count)
max_setup_count = smb_xa_max_setup_count;
if (max_parameter_count > smb_xa_max_param_count)
max_parameter_count = smb_xa_max_param_count;
if (max_data_count > smb_xa_max_data_count)
max_data_count = smb_xa_max_data_count;
xa = kmem_zalloc(sizeof (smb_xa_t), KM_SLEEP);
xa->xa_refcnt = 1;
xa->smb_com = sr->smb_com;
xa->smb_flg = sr->smb_flg;
xa->smb_flg2 = sr->smb_flg2;
xa->smb_tid = sr->smb_tid;
xa->smb_pid = sr->smb_pid;
xa->smb_uid = sr->smb_uid;
xa->xa_smb_mid = sr->smb_mid;
xa->xa_smb_fid = 0xFFFF;
xa->reply_seqnum = sr->reply_seqnum;
xa->smb_tpscnt = total_parameter_count;
xa->smb_tdscnt = total_data_count;
xa->smb_mprcnt = max_parameter_count;
xa->smb_mdrcnt = max_data_count;
xa->smb_msrcnt = max_setup_count;
xa->smb_suwcnt = setup_word_count;
xa->xa_session = session;
xa->xa_magic = SMB_XA_MAGIC;
/* request parts */
xa->req_setup_mb.max_bytes = setup_word_count * 2;
xa->req_param_mb.max_bytes = total_parameter_count;
xa->req_data_mb.max_bytes = total_data_count;
/* reply parts */
xa->rep_setup_mb.max_bytes = max_setup_count * 2;
xa->rep_param_mb.max_bytes = max_parameter_count;
xa->rep_data_mb.max_bytes = max_data_count;
/*
* The new xa structure is checked against the current list to see
* if it exists already.
*/
xlist = &session->s_xa_list;
smb_llist_enter(xlist, RW_WRITER);
nxa = smb_llist_head(xlist);
while (nxa) {
ASSERT(nxa->xa_magic == SMB_XA_MAGIC);
if (nxa->xa_smb_mid == xa->xa_smb_mid &&
nxa->smb_pid == xa->smb_pid &&
!SMB_XA_CLOSED(nxa) &&
!(nxa->xa_flags & SMB_XA_FLAG_COMPLETE)) {
smb_llist_exit(xlist);
kmem_free(xa, sizeof (smb_xa_t));
return (NULL);
}
nxa = smb_llist_next(xlist, nxa);
}
smb_llist_insert_tail(xlist, xa);
smb_llist_exit(xlist);
return (xa);
}
void
smb_xa_delete(smb_xa_t *xa)
{
ASSERT(xa->xa_refcnt == 0);
ASSERT(SMB_XA_CLOSED(xa));
if (xa->xa_pipe_name)
smb_mem_free(xa->xa_pipe_name);
/* request parts */
if (xa->req_setup_mb.chain != NULL)
m_freem(xa->req_setup_mb.chain);
if (xa->req_param_mb.chain != NULL)
m_freem(xa->req_param_mb.chain);
if (xa->req_data_mb.chain != NULL)
m_freem(xa->req_data_mb.chain);
/* reply parts */
if (xa->rep_setup_mb.chain != NULL)
m_freem(xa->rep_setup_mb.chain);
if (xa->rep_param_mb.chain != NULL)
m_freem(xa->rep_param_mb.chain);
if (xa->rep_data_mb.chain != NULL)
m_freem(xa->rep_data_mb.chain);
xa->xa_magic = (uint32_t)~SMB_XA_MAGIC;
kmem_free(xa, sizeof (smb_xa_t));
}
smb_xa_t *
smb_xa_hold(smb_xa_t *xa)
{
mutex_enter(&xa->xa_mutex);
xa->xa_refcnt++;
ASSERT(xa->xa_refcnt);
mutex_exit(&xa->xa_mutex);
return (xa);
}
void
smb_xa_rele(smb_session_t *session, smb_xa_t *xa)
{
mutex_enter(&xa->xa_mutex);
ASSERT(xa->xa_refcnt);
xa->xa_refcnt--;
if (SMB_XA_CLOSED(xa) && (xa->xa_refcnt == 0)) {
mutex_exit(&xa->xa_mutex);
smb_llist_enter(&session->s_xa_list, RW_WRITER);
smb_llist_remove(&session->s_xa_list, xa);
smb_llist_exit(&session->s_xa_list);
smb_xa_delete(xa);
return;
}
mutex_exit(&xa->xa_mutex);
}
int
smb_xa_open(smb_xa_t *xa)
{
int rc;
mutex_enter(&xa->xa_mutex);
ASSERT((xa->xa_flags & SMB_XA_FLAG_OPEN) == 0);
if ((xa->xa_flags & SMB_XA_FLAG_CLOSE) == 0) {
xa->xa_flags |= SMB_XA_FLAG_OPEN;
rc = 0;
} else {
rc = ERROR_INVALID_HANDLE;
}
mutex_exit(&xa->xa_mutex);
return (rc);
}
void
smb_xa_close(smb_xa_t *xa)
{
mutex_enter(&xa->xa_mutex);
xa->xa_flags |= SMB_XA_FLAG_CLOSE;
xa->xa_flags &= ~SMB_XA_FLAG_OPEN;
if (xa->xa_refcnt == 0) {
mutex_exit(&xa->xa_mutex);
smb_llist_enter(&xa->xa_session->s_xa_list, RW_WRITER);
smb_llist_remove(&xa->xa_session->s_xa_list, xa);
smb_llist_exit(&xa->xa_session->s_xa_list);
smb_xa_delete(xa);
return;
}
mutex_exit(&xa->xa_mutex);
}
int
smb_xa_complete(smb_xa_t *xa)
{
int rc;
mutex_enter(&xa->xa_mutex);
if (xa->xa_flags & (SMB_XA_FLAG_COMPLETE | SMB_XA_FLAG_CLOSE)) {
rc = 0; /* error ("not complete") */
} else {
rc = 1; /* Yes, "complete" */
xa->xa_flags |= SMB_XA_FLAG_COMPLETE;
/*
* During trans & trans-secondary processing,
* we copied the request data into these.
* Now we want to parse them, so we need to
* move the "finger" back to the beginning.
*/
xa->req_setup_mb.chain_offset = 0;
xa->req_param_mb.chain_offset = 0;
xa->req_data_mb.chain_offset = 0;
}
mutex_exit(&xa->xa_mutex);
return (rc);
}
smb_xa_t *
smb_xa_find(
smb_session_t *session,
uint32_t pid,
uint16_t mid)
{
smb_xa_t *xa;
smb_llist_t *xlist;
xlist = &session->s_xa_list;
smb_llist_enter(xlist, RW_READER);
xa = smb_llist_head(xlist);
while (xa) {
mutex_enter(&xa->xa_mutex);
if (xa->xa_smb_mid == mid &&
xa->smb_pid == pid &&
!SMB_XA_CLOSED(xa) &&
!(xa->xa_flags & SMB_XA_FLAG_COMPLETE)) {
xa->xa_refcnt++;
ASSERT(xa->xa_refcnt);
mutex_exit(&xa->xa_mutex);
break;
}
mutex_exit(&xa->xa_mutex);
xa = smb_llist_next(xlist, xa);
}
smb_llist_exit(xlist);
return (xa);
}