#include <sys/smb_apple.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_rq.h>
#include <netsmb/smb_dev.h>
#include <netsmb/smb_converter.h>
int
smb_usr_negotiate(struct smbioc_negotiate *vspec, vfs_context_t context,
struct smb_dev *sdp, int searchOnly)
{
struct smb_vc *vcp;
int error;
uint32_t usersMaxBufferLen = vspec->ioc_negotiate_token_len;
if (! vfs_context_is64bit (context)) {
vspec->ioc_kern_saddr = CAST_USER_ADDR_T(vspec->ioc_saddr);
vspec->ioc_kern_laddr = CAST_USER_ADDR_T(vspec->ioc_laddr);
}
error = smb_sm_negotiate(vspec, context, &sdp->sd_vc, sdp, searchOnly);
if (error) {
vspec->ioc_errno = error;
return 0;
}
vcp = sdp->sd_vc;
vspec->ioc_ret_caps = VC_CAPS(vcp);
vspec->ioc_ret_vc_flags = vcp->vc_flags;
vspec->ioc_negotiate_token_len = vcp->negotiate_tokenlen;
if ((vcp->negotiate_token) && (usersMaxBufferLen >= vcp->negotiate_tokenlen)) {
user_addr_t uaddr = vspec->ioc_negotiate_token;
error = copyout(vcp->negotiate_token, uaddr, (size_t)vcp->negotiate_tokenlen);
vspec->ioc_errno = error;
}
if (vspec->ioc_extra_flags & SMB_SHARING_VC) {
if (vcp->vc_username) {
strlcpy(vspec->ioc_user, vcp->vc_username, sizeof(vspec->ioc_user));
}
vspec->ioc_max_client_size = vcp->vc_gss.gss_cpn_len;
vspec->ioc_max_target_size = vcp->vc_gss.gss_spn_len;
}
return error;
}
int
smb_usr_simplerequest(struct smb_share *share, struct smbioc_rq *dp,
vfs_context_t context)
{
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
struct mdchain *mdp;
int32_t response_size;
int error;
switch (dp->ioc_cmd) {
case SMB_COM_CLOSE_AND_TREE_DISC:
case SMB_COM_TREE_CONNECT:
case SMB_COM_TREE_DISCONNECT:
case SMB_COM_NEGOTIATE:
case SMB_COM_SESSION_SETUP_ANDX:
case SMB_COM_LOGOFF_ANDX:
case SMB_COM_TREE_CONNECT_ANDX:
return EPERM;
}
if (! vfs_context_is64bit (context)) {
dp->ioc_kern_twords = CAST_USER_ADDR_T(dp->ioc_twords);
dp->ioc_kern_tbytes = CAST_USER_ADDR_T(dp->ioc_tbytes);
dp->ioc_kern_rpbuf = CAST_USER_ADDR_T(dp->ioc_rpbuf);
}
error = smb_rq_init(rqp, SSTOCP(share), dp->ioc_cmd, dp->ioc_flags2, context);
if (error)
return error;
mbp = &rqp->sr_rq;
smb_rq_wstart(rqp);
error = mb_put_user_mem(mbp, dp->ioc_kern_twords, dp->ioc_twc * 2, 0, context);
if (error)
goto bad;
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
error = mb_put_user_mem(mbp, dp->ioc_kern_tbytes, dp->ioc_tbc, 0, context);
if (error)
goto bad;
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
if (error)
goto bad;
mdp = &rqp->sr_rp;
response_size = (int32_t)md_get_size(mdp);
if (response_size > dp->ioc_rpbufsz) {
error = EBADRPC;
goto bad;
}
error = md_get_user_mem(mdp, dp->ioc_kern_rpbuf, response_size, 0, context);
if (error)
goto bad;
dp->ioc_rpbufsz = response_size;
bad:
dp->ioc_ntstatus = rqp->sr_ntstatus;
dp->ioc_errno = error;
dp->ioc_flags = rqp->sr_rpflags;
dp->ioc_flags2 = rqp->sr_rpflags2;
smb_rq_done(rqp);
return 0;
}
static int
smb_cpdatain(struct mbchain *mbp, user_addr_t data, int len, vfs_context_t context)
{
int error;
if (len == 0)
return 0;
error = mb_init(mbp);
if (! error)
error = mb_put_user_mem(mbp, data, len, 0, context);
return error;
}
int
smb_usr_t2request(struct smb_share *share, struct smbioc_t2rq *dp, vfs_context_t context)
{
struct smb_t2rq t2, *t2p = &t2;
struct mdchain *mdp;
int error;
uint16_t len;
if (dp->ioc_setupcnt > SMB_MAXSETUPWORDS)
return EINVAL;
error = smb_t2_init(t2p, SSTOCP(share), dp->ioc_setup, dp->ioc_setupcnt, context);
if (error) {
return error;
}
len = t2p->t2_setupcount = dp->ioc_setupcnt;
if (len > 1)
t2p->t2_setupdata = dp->ioc_setup;
if (! vfs_context_is64bit (context)) {
dp->ioc_kern_name = CAST_USER_ADDR_T(dp->ioc_name);
dp->ioc_kern_tparam = CAST_USER_ADDR_T(dp->ioc_tparam);
dp->ioc_kern_tdata = CAST_USER_ADDR_T(dp->ioc_tdata);
dp->ioc_kern_rparam = CAST_USER_ADDR_T(dp->ioc_rparam);
dp->ioc_kern_rdata = CAST_USER_ADDR_T(dp->ioc_rdata);
}
if (dp->ioc_kern_name && dp->ioc_name_len) {
t2p->t_name = smb_memdupin(dp->ioc_kern_name, dp->ioc_name_len);
if (t2p->t_name == NULL) {
error = ENOMEM;
goto bad;
}
}
t2p->t2_maxscount = 0;
t2p->t2_maxpcount = dp->ioc_rparamcnt;
t2p->t2_maxdcount = dp->ioc_rdatacnt;
error = smb_cpdatain(&t2p->t2_tparam, dp->ioc_kern_tparam, dp->ioc_tparamcnt, context);
if (! error)
error = smb_cpdatain(&t2p->t2_tdata, dp->ioc_kern_tdata, dp->ioc_tdatacnt, context);
if (error) {
goto bad;
}
error = smb_t2_request(t2p);
dp->ioc_ntstatus = t2p->t2_ntstatus;
dp->ioc_flags2 = t2p->t2_sr_rpflags2;
if (error) {
goto bad;
}
mdp = &t2p->t2_rparam;
if (mdp->md_top) {
len = m_fixhdr(mdp->md_top);
if (len > dp->ioc_rparamcnt) {
error = EMSGSIZE;
goto bad;
}
dp->ioc_rparamcnt = len;
error = md_get_user_mem(mdp, dp->ioc_kern_rparam, len, 0, context);
if (error) {
goto bad;
}
} else
dp->ioc_rparamcnt = 0;
mdp = &t2p->t2_rdata;
if (mdp->md_top) {
len = m_fixhdr(mdp->md_top);
if (len > dp->ioc_rdatacnt) {
error = EMSGSIZE;
goto bad;
}
dp->ioc_rdatacnt = len;
error = md_get_user_mem(mdp, dp->ioc_kern_rdata, len, 0, context);
} else
dp->ioc_rdatacnt = 0;
bad:
SMB_FREE(t2p->t_name, M_SMBSTR);
smb_t2_done(t2p);
return error;
}
int smb_usr_convert_path_to_network(struct smb_vc *vc, struct smbioc_path_convert * dp)
{
size_t ntwrk_len = (size_t)dp->ioc_dest_len;
char *network = NULL;
char *utf8str = NULL;
int error;
utf8str = smb_memdupin(dp->ioc_kern_src, dp->ioc_src_len);
if (utf8str) {
SMB_MALLOC(network, char *, ntwrk_len, M_SMBSTR, M_WAITOK | M_ZERO);
}
if ((utf8str == NULL) || (network == NULL)) {
error = ENOMEM;
goto done;
}
error = smb_convert_path_to_network(utf8str, dp->ioc_src_len, network, &ntwrk_len,
'\\', (int)dp->ioc_flags, SMB_UNICODE_STRINGS(vc));
if (error) {
SMBERROR("converter failed : %d\n", error);
SMBDEBUG("utf8str = %s src len = %d dest len = %d\n", utf8str,
(int)dp->ioc_src_len, (int)dp->ioc_dest_len);
goto done;
}
error = copyout(network, dp->ioc_kern_dest, ntwrk_len);
if (error) {
SMBERROR("copyout failed : %d\n", error);
smb_hexdump(__FUNCTION__, "dest buffer: ", (u_char *)network, ntwrk_len);
goto done;
}
dp->ioc_dest_len = (uint32_t)ntwrk_len;
done:
if (utf8str)
SMB_FREE(utf8str, M_SMBSTR);
if (network)
SMB_FREE(network, M_SMBSTR);
return error;
}
int smb_usr_convert_network_to_path(struct smb_vc *vc, struct smbioc_path_convert * dp)
{
size_t utf8str_len = (size_t)dp->ioc_dest_len;
char *network = NULL;
char *utf8str = NULL;
int error;
network = smb_memdupin(dp->ioc_kern_src, dp->ioc_src_len);
if (network) {
SMB_MALLOC(utf8str, char *, utf8str_len, M_SMBSTR, M_WAITOK | M_ZERO);
}
if ((utf8str == NULL) || (network == NULL)) {
error = ENOMEM;
goto done;
}
error = smb_convert_network_to_path(network, dp->ioc_src_len, utf8str,
&utf8str_len, '\\', (int)dp->ioc_flags,
SMB_UNICODE_STRINGS(vc));
if (error) {
SMBERROR("converter failed : %d\n", error);
smb_hexdump(__FUNCTION__, "source buffer: ", (u_char *)network, dp->ioc_src_len);
goto done;
}
error = copyout(utf8str, dp->ioc_kern_dest, utf8str_len);
if (error) {
SMBERROR("copyout failed : %d\n", error);
SMBDEBUG("utf8str = %s src len = %d dest len = %d\n", utf8str,
(int)dp->ioc_src_len, (int)dp->ioc_dest_len);
goto done;
}
dp->ioc_dest_len = (uint32_t)utf8str_len;
done:
if (utf8str)
SMB_FREE(utf8str, M_SMBSTR);
if (network)
SMB_FREE(network, M_SMBSTR);
return error;
}
int smb_usr_set_network_identity(struct smb_vc *vcp, struct smbioc_ntwrk_identity *ntwrkID)
{
if (vcp->vc_flags & SMBV_NETWORK_SID) {
return EEXIST;
}
if (ntwrkID->ioc_ntsid_len != sizeof(ntsid_t)) {
return EINVAL;
}
memcpy(&vcp->vc_ntwrk_sid, &ntwrkID->ioc_ntsid, (size_t)ntwrkID->ioc_ntsid_len);
vcp->vc_flags |= SMBV_NETWORK_SID;
return 0;
}
int
smb_usr_fsctl(struct smb_share *share, struct smbioc_fsctl *fsctl, vfs_context_t context)
{
struct smb_ntrq * ntp = NULL;
int error = 0;
if (fsctl->ioc_tdatacnt > INT_MAX) {
error = EINVAL;
goto done;
}
fsctl->ioc_errno = 0;
fsctl->ioc_ntstatus = 0;
if (! vfs_context_is64bit (context)) {
fsctl->ioc_kern_tdata = CAST_USER_ADDR_T(fsctl->ioc_tdata);
fsctl->ioc_kern_rdata = CAST_USER_ADDR_T(fsctl->ioc_rdata);
}
error = smb_nt_alloc(SSTOCP(share), NT_TRANSACT_IOCTL, context, &ntp);
if (error) {
goto done;
}
mb_init(&ntp->nt_tsetup);
mb_put_uint32le(&ntp->nt_tsetup, fsctl->ioc_fsctl);
mb_put_uint16le(&ntp->nt_tsetup, fsctl->ioc_fh);
mb_put_uint8(&ntp->nt_tsetup, 1);
mb_put_uint8(&ntp->nt_tsetup, 0);
if (fsctl->ioc_tdatacnt) {
mb_init(&ntp->nt_tdata);
error = mb_put_user_mem(&ntp->nt_tdata, fsctl->ioc_kern_tdata,
fsctl->ioc_tdatacnt, 0, context);
if (error) {
goto done;
}
}
ntp->nt_maxpcount = 0;
ntp->nt_maxdcount = fsctl->ioc_rdatacnt;
fsctl->ioc_errno = smb_nt_request(ntp);
fsctl->ioc_ntstatus = ntp->nt_status;
if (fsctl->ioc_errno) {
error = 0;
goto done;
}
if (ntp->nt_flags & SMBT2_MOREDATA) {
fsctl->ioc_errno = ENOSPC;
error = 0;
goto done;
}
if (ntp->nt_rdata.md_top) {
size_t datalen = m_fixhdr(ntp->nt_rdata.md_top);
if (datalen > fsctl->ioc_rdatacnt) {
SMBERROR("datalen(%u) > ioc_rdatacnt(%u)",
(unsigned)datalen, (unsigned)fsctl->ioc_rdatacnt);
error = EMSGSIZE;
goto done;
}
fsctl->ioc_rdatacnt = (uint32_t)datalen;
error = md_get_user_mem(&ntp->nt_rdata, fsctl->ioc_kern_rdata,
(uint32_t)datalen, 0, context);
if (error) {
SMBERROR("md_get_user_mem failed with %d", error);
goto done;
}
} else {
fsctl->ioc_rdatacnt = 0;
}
done:
if (ntp) {
smb_nt_done(ntp);
}
return error;
}