#include "smbclient.h"
#include "smbclient_private.h"
#include "ntstatus.h"
#include <netsmb/upi_mbuf.h>
#include <netsmb/smbio.h>
#include <netsmb/smb_dev.h>
#include <libkern/OSByteOrder.h>
struct smb_header {
uint32_t protocol_id;
uint8_t command;
uint32_t status;
uint8_t flags;
uint16_t flags2;
uint16_t pid_high;
uint8_t signature[8];
uint16_t reserved;
uint16_t tid;
uint16_t pid;
uint16_t uid;
uint16_t mid;
} __attribute__((__packed__));
#define ADVANCE(pointer, nbytes) do { \
(pointer) = (typeof(pointer)) ( ((const uint8_t *)(pointer)) + (nbytes) ); \
} while (0)
static int
smb_ioc_request(
void * hContext,
struct smb_header * header,
const mbuf_t words,
const mbuf_t bytes,
mbuf_t response)
{
struct smbioc_rq krq;
bzero(&krq, sizeof(krq));
krq.ioc_version = SMB_IOC_STRUCT_VERSION;
krq.ioc_cmd = header->command;
krq.ioc_twc = mbuf_len(words) / sizeof(uint16_t);
krq.ioc_twords = mbuf_data(words);
krq.ioc_tbc = mbuf_len(bytes);
krq.ioc_tbytes = mbuf_data(bytes);
krq.ioc_rpbufsz = (int32_t)mbuf_maxlen(response);
krq.ioc_rpbuf = mbuf_data(response);
if (smb_ioctl_call(((struct smb_ctx *)hContext)->ct_fd,
SMBIOC_REQUEST, &krq) == -1) {
return errno;
}
header->flags = krq.ioc_flags;
header->flags2 = krq.ioc_flags2;
header->status = krq.ioc_ntstatus;
mbuf_setlen(response, krq.ioc_rpbufsz);
return 0;
}
NTSTATUS
SMBRawTransaction(
SMBHANDLE inConnection,
const void *lpInBuffer,
size_t nInBufferSize,
void *lpOutBuffer,
size_t nOutBufferSize,
size_t *lpBytesRead)
{
NTSTATUS status;
int err;
void * hContext;
struct smb_header header;
mbuf_t words = NULL;
mbuf_t bytes = NULL;
mbuf_t response = NULL;
size_t len;
const void * ptr;
bzero(&header, sizeof(header));
status = SMBServerContext(inConnection, &hContext);
if (!NT_SUCCESS(status)) {
return status;
}
if ((size_t)nInBufferSize < sizeof(struct smb_header) ||
(size_t)nOutBufferSize < sizeof(struct smb_header)) {
err = STATUS_BUFFER_TOO_SMALL;
goto errout;
}
ptr = lpInBuffer;
ADVANCE(ptr, sizeof(uint32_t));
header.command = *(uint8_t *)ptr;
if ((size_t)nInBufferSize < (sizeof(struct smb_header) + sizeof(uint8_t))) {
err = STATUS_BUFFER_TOO_SMALL;
goto errout;
}
ptr = lpInBuffer;
ADVANCE(ptr, sizeof(struct smb_header));
len = (*(uint8_t *)ptr) * sizeof(uint16_t);
ADVANCE(ptr, sizeof(uint8_t));
if (mbuf_attachcluster(MBUF_WAITOK, MBUF_TYPE_DATA, &words, (void *)ptr, NULL, len, NULL)) {
err = STATUS_NO_MEMORY;
goto errout;
}
mbuf_setlen(words, len);
ADVANCE(ptr, len);
if ((uintptr_t)ptr > ((uintptr_t)lpInBuffer + nInBufferSize) ||
(uintptr_t)ptr < (uintptr_t)mbuf_data(words)) {
err = STATUS_MARSHALL_OVERFLOW;
goto errout;
}
len = OSReadLittleInt16(ptr, 0);
ADVANCE(ptr, sizeof(uint16_t));
if (mbuf_attachcluster(MBUF_WAITOK, MBUF_TYPE_DATA, &bytes, (void *)ptr, NULL, len, NULL)) {
err = STATUS_NO_MEMORY;
goto errout;
}
mbuf_setlen(bytes, len);
ADVANCE(ptr, len);
if ((uintptr_t)ptr > ((uintptr_t)lpInBuffer + nInBufferSize) ||
(uintptr_t)ptr < (uintptr_t)mbuf_data(bytes)) {
err = STATUS_MARSHALL_OVERFLOW;
goto errout;
}
len = (size_t)(nOutBufferSize - sizeof(struct smb_header));
if (mbuf_attachcluster(MBUF_WAITOK, MBUF_TYPE_DATA, &response, ((uint8_t *)lpOutBuffer + sizeof(struct smb_header)), NULL, len, NULL)) {
err = STATUS_NO_MEMORY;
goto errout;
}
err = smb_ioc_request(hContext, &header, words, bytes, response);
if (err) {
err = STATUS_IO_DEVICE_ERROR;
goto errout;
}
memcpy(lpOutBuffer, lpInBuffer, sizeof(header));
OSWriteLittleInt32(lpOutBuffer, 0, *(const uint32_t *)(void *)SMB_SIGNATURE);
OSWriteLittleInt32(lpOutBuffer, offsetof(struct smb_header, status),
header.status);
OSWriteLittleInt16(lpOutBuffer, offsetof(struct smb_header, flags),
header.flags);
OSWriteLittleInt32(lpOutBuffer, offsetof(struct smb_header, flags2),
header.flags2);
*lpBytesRead = mbuf_len(response) + sizeof(struct smb_header);
err = STATUS_SUCCESS;
errout:
if (words)
mbuf_freem(words);
if (bytes)
mbuf_freem(bytes);
if (response)
mbuf_freem(response);
return err;
}