summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsmbfs/smb/rq.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libsmbfs/smb/rq.c')
-rw-r--r--usr/src/lib/libsmbfs/smb/rq.c381
1 files changed, 322 insertions, 59 deletions
diff --git a/usr/src/lib/libsmbfs/smb/rq.c b/usr/src/lib/libsmbfs/smb/rq.c
index 6390a3c157..7b21708428 100644
--- a/usr/src/lib/libsmbfs/smb/rq.c
+++ b/usr/src/lib/libsmbfs/smb/rq.c
@@ -47,26 +47,66 @@
#include <sysexits.h>
#include <libintl.h>
+#include <netsmb/smb.h>
#include <netsmb/smb_lib.h>
#include "private.h"
+static uint32_t smb_map_doserr(uint8_t, uint16_t);
+/*
+ * Create and initialize a request structure, for either an
+ * "internal" request (one that does not use the driver) or
+ * a regular "driver" request, that uses driver ioctls.
+ *
+ * The two kinds are built a little differently:
+ * Driver requests are composed starting with the
+ * first word of the "variable word vector" section.
+ * The driver prepends the SMB header and word count.
+ * The driver also needs an output buffer to receive
+ * the response, filled in via copyout in the ioctl.
+ *
+ * Internal requests are composed entirely in this library.
+ * Space for the SMB header is reserved here, and later
+ * filled in by smb_rq_internal before the send/receive.
+ */
int
-smb_rq_init(struct smb_ctx *ctx, uchar_t cmd, size_t rpbufsz,
- struct smb_rq **rqpp)
+smb_rq_init(struct smb_ctx *ctx, uchar_t cmd, struct smb_rq **rqpp)
{
struct smb_rq *rqp;
rqp = malloc(sizeof (*rqp));
if (rqp == NULL)
- return (ENOMEM);
+ goto errout;
bzero(rqp, sizeof (*rqp));
rqp->rq_cmd = cmd;
rqp->rq_ctx = ctx;
- mb_init(&rqp->rq_rq, M_MINSIZE);
- mb_init(&rqp->rq_rp, rpbufsz);
+
+ /*
+ * Setup the request buffer.
+ * Do the reply buffer later.
+ */
+ if (mb_init(&rqp->rq_rq, M_MINSIZE))
+ goto errout;
+
+ /* Space for the SMB header. (filled in later) */
+ mb_put_mem(&rqp->rq_rq, NULL, SMB_HDRLEN);
+
+ /*
+ * Copy the ctx flags here, so the caller can
+ * update the req flags before the OTW call.
+ */
+ rqp->rq_hflags = ctx->ct_hflags;
+ rqp->rq_hflags2 = ctx->ct_hflags2;
+
*rqpp = rqp;
return (0);
+
+errout:
+ if (rqp) {
+ smb_rq_done(rqp);
+ free(rqp);
+ }
+ return (ENOMEM);
}
void
@@ -77,81 +117,162 @@ smb_rq_done(struct smb_rq *rqp)
free(rqp);
}
+/*
+ * Reserve space for the word count, which is filled in later by
+ * smb_rq_wend(). Also initialize the counter that it uses
+ * to figure out what value to fill in.
+ *
+ * Note that the word count happens to be 8-bits,
+ * which can lead to confusion.
+ */
void
-smb_rq_wend(struct smb_rq *rqp)
+smb_rq_wstart(struct smb_rq *rqp)
{
- if (rqp->rq_rq.mb_count & 1)
- smb_error(dgettext(TEXT_DOMAIN,
- "smbrq_wend: odd word count\n"), 0);
- rqp->rq_wcount = rqp->rq_rq.mb_count / 2;
- rqp->rq_rq.mb_count = 0;
+ struct mbdata *mbp = &rqp->rq_rq;
+
+ mb_fit(mbp, 1, &rqp->rq_wcntp);
+ rqp->rq_wcbase = mbp->mb_count;
}
-int
-smb_rq_dmem(struct mbdata *mbp, const char *src, size_t size)
+/*
+ * Fill in the word count, in the space reserved by
+ * smb_rq_wstart().
+ */
+void
+smb_rq_wend(struct smb_rq *rqp)
{
- struct mbuf *m;
- char *dst;
- int cplen, error;
+ struct mbdata *mbp = &rqp->rq_rq;
+ int wcnt;
- if (size == 0)
- return (0);
- m = mbp->mb_cur;
- if ((error = m_getm(m, size, &m)) != 0)
- return (error);
- while (size > 0) {
- cplen = M_TRAILINGSPACE(m);
- if (cplen == 0) {
- m = m->m_next;
- continue;
- }
- if (cplen > (int)size)
- cplen = size;
- dst = mtod(m, char *) + m->m_len;
- nls_mem_toext(dst, src, cplen);
- size -= cplen;
- src += cplen;
- m->m_len += cplen;
- mbp->mb_count += cplen;
+ if (rqp->rq_wcntp == NULL) {
+ DPRINT("no wcount ptr\n");
+ return;
}
- mbp->mb_pos = mtod(m, char *) + m->m_len;
- mbp->mb_cur = m;
- return (0);
+ wcnt = mbp->mb_count - rqp->rq_wcbase;
+ if (wcnt > 0x1ff)
+ DPRINT("word count too large (%d)\n", wcnt);
+ if (wcnt & 1)
+ DPRINT("odd word count\n");
+ wcnt >>= 1;
+
+ /*
+ * Fill in the word count (8-bits).
+ * Also store it in the rq, in case
+ * we're using the ioctl path.
+ */
+ *rqp->rq_wcntp = (char)wcnt;
}
-int
-smb_rq_dstring(struct mbdata *mbp, const char *s)
+/*
+ * Reserve space for the byte count, which is filled in later by
+ * smb_rq_bend(). Also initialize the counter that it uses
+ * to figure out what value to fill in.
+ *
+ * Note that the byte count happens to be 16-bits,
+ * which can lead to confusion.
+ */
+void
+smb_rq_bstart(struct smb_rq *rqp)
+{
+ struct mbdata *mbp = &rqp->rq_rq;
+
+ mb_fit(mbp, 2, &rqp->rq_bcntp);
+ rqp->rq_bcbase = mbp->mb_count;
+}
+
+/*
+ * Fill in the byte count, in the space reserved by
+ * smb_rq_bstart().
+ */
+void
+smb_rq_bend(struct smb_rq *rqp)
{
- return (smb_rq_dmem(mbp, s, strlen(s) + 1));
+ struct mbdata *mbp = &rqp->rq_rq;
+ int bcnt;
+
+ if (rqp->rq_bcntp == NULL) {
+ DPRINT("no bcount ptr\n");
+ return;
+ }
+ bcnt = mbp->mb_count - rqp->rq_bcbase;
+ if (bcnt > 0xffff)
+ DPRINT("byte count too large (%d)\n", bcnt);
+ /*
+ * Fill in the byte count (16-bits).
+ * Also store it in the rq, in case
+ * we're using the ioctl path.
+ *
+ * The pointer is char * type due to
+ * typical off-by-one alignment.
+ */
+ rqp->rq_bcntp[0] = bcnt & 0xFF;
+ rqp->rq_bcntp[1] = (bcnt >> 8);
}
+/*
+ * Removed: smb_rq_dmem
+ * which was mostly like: mb_put_mem
+ */
+
int
smb_rq_simple(struct smb_rq *rqp)
{
struct smbioc_rq krq;
struct mbdata *mbp;
char *data;
- int i;
+ uint32_t len;
+ size_t rpbufsz;
+
+ bzero(&krq, sizeof (krq));
+ krq.ioc_cmd = rqp->rq_cmd;
+ /*
+ * Make the SMB request body contiguous,
+ * and fill in the ioctl request.
+ */
mbp = smb_rq_getrequest(rqp);
m_lineup(mbp->mb_top, &mbp->mb_top);
data = mtod(mbp->mb_top, char *);
- bzero(&krq, sizeof (krq));
- krq.ioc_cmd = rqp->rq_cmd;
- krq.ioc_twc = rqp->rq_wcount;
- krq.ioc_twords = data;
- krq.ioc_tbc = mbp->mb_count;
- krq.ioc_tbytes = data + rqp->rq_wcount * 2;
+ len = m_totlen(mbp->mb_top);
+ /*
+ * _rq_init left space for the SMB header,
+ * which makes mb_count the offset from
+ * the beginning of the header (useful).
+ * However, in this code path the driver
+ * prepends the header, so we skip it.
+ */
+ krq.ioc_tbufsz = len - SMB_HDRLEN;
+ krq.ioc_tbuf = data + SMB_HDRLEN;
+
+ /*
+ * Setup a buffer to hold the reply.
+ *
+ * Default size is M_MINSIZE, but the
+ * caller may increase rq_rpbufsz
+ * before calling this.
+ */
mbp = smb_rq_getreply(rqp);
- krq.ioc_rpbufsz = mbp->mb_top->m_maxlen;
- krq.ioc_rpbuf = mtod(mbp->mb_top, char *);
- if (ioctl(rqp->rq_ctx->ct_fd, SMBIOC_REQUEST, &krq) == -1) {
+ rpbufsz = rqp->rq_rpbufsz;
+ if (rpbufsz < M_MINSIZE)
+ rpbufsz = M_MINSIZE;
+ if (mb_init(mbp, rpbufsz))
+ return (ENOMEM);
+ krq.ioc_rbufsz = rpbufsz;
+ krq.ioc_rbuf = mtod(mbp->mb_top, char *);
+
+ /*
+ * Call the driver
+ */
+ if (ioctl(rqp->rq_ctx->ct_dev_fd, SMBIOC_REQUEST, &krq) == -1)
return (errno);
- }
- mbp->mb_top->m_len = krq.ioc_rwc * 2 + krq.ioc_rbc;
- rqp->rq_wcount = krq.ioc_rwc;
- rqp->rq_bcount = krq.ioc_rbc;
+
+ /*
+ * Initialize returned mbdata.
+ * SMB header already parsed.
+ */
+ mbp->mb_top->m_len = krq.ioc_rbufsz;
+
return (0);
}
@@ -167,13 +288,11 @@ smb_t2_request(struct smb_ctx *ctx, int setupcount, uint16_t *setup,
{
smbioc_t2rq_t *krq;
int i;
- char *pass;
-
krq = (smbioc_t2rq_t *)malloc(sizeof (smbioc_t2rq_t));
bzero(krq, sizeof (*krq));
- if (setupcount < 0 || setupcount >= SMB_MAXSETUPWORDS) {
+ if (setupcount < 0 || setupcount >= SMBIOC_T2RQ_MAXSETUP) {
/* Bogus setup count, or too many setup words */
return (EINVAL);
}
@@ -191,7 +310,7 @@ smb_t2_request(struct smb_ctx *ctx, int setupcount, uint16_t *setup,
krq->ioc_rparam = rparam;
krq->ioc_rdata = rdata;
- if (ioctl(ctx->ct_fd, SMBIOC_T2RQ, krq) == -1) {
+ if (ioctl(ctx->ct_dev_fd, SMBIOC_T2RQ, krq) == -1) {
return (errno);
}
@@ -200,5 +319,149 @@ smb_t2_request(struct smb_ctx *ctx, int setupcount, uint16_t *setup,
*buffer_oflow = (krq->ioc_rpflags2 & SMB_FLAGS2_ERR_STATUS) &&
(krq->ioc_error == NT_STATUS_BUFFER_OVERFLOW);
free(krq);
+
return (0);
}
+
+
+/*
+ * Do an over-the-wire call without using the nsmb driver.
+ * This is all "internal" to this library, and used only
+ * for connection setup (negotiate protocol, etc.)
+ */
+int
+smb_rq_internal(struct smb_ctx *ctx, struct smb_rq *rqp)
+{
+ static const uint8_t ffsmb[4] = SMB_SIGNATURE;
+ struct smb_iods *is = &ctx->ct_iods;
+ uint32_t sigbuf[2];
+ struct mbdata mbtmp, *mbp;
+ int err, save_mlen;
+ uint8_t ctmp;
+
+ rqp->rq_uid = is->is_smbuid;
+ rqp->rq_tid = SMB_TID_UNKNOWN;
+ rqp->rq_mid = is->is_next_mid++;
+
+ /*
+ * Fill in the NBT and SMB headers
+ * Using mbtmp so we can rewind without
+ * affecting the passed request mbdata.
+ */
+ bcopy(&rqp->rq_rq, &mbtmp, sizeof (mbtmp));
+ mbp = &mbtmp;
+ mbp->mb_cur = mbp->mb_top;
+ mbp->mb_pos = mbp->mb_cur->m_data;
+ mbp->mb_count = 0;
+ /* Have to save and restore m_len */
+ save_mlen = mbp->mb_cur->m_len;
+ mbp->mb_cur->m_len = 0;
+
+ /*
+ * rewind done; fill it in
+ */
+ mb_put_mem(mbp, (char *)SMB_SIGNATURE, SMB_SIGLEN);
+ mb_put_uint8(mbp, rqp->rq_cmd);
+ mb_put_mem(mbp, NULL, 4); /* status */
+ mb_put_uint8(mbp, rqp->rq_hflags);
+ mb_put_uint16le(mbp, rqp->rq_hflags2);
+ mb_put_uint16le(mbp, 0); /* pid_hi */
+ mb_put_mem(mbp, NULL, 8); /* signature */
+ mb_put_uint16le(mbp, 0); /* reserved */
+ mb_put_uint16le(mbp, rqp->rq_tid);
+ mb_put_uint16le(mbp, 0); /* pid_lo */
+ mb_put_uint16le(mbp, rqp->rq_uid);
+ mb_put_uint16le(mbp, rqp->rq_mid);
+
+ /* Restore original m_len */
+ mbp->mb_cur->m_len = save_mlen;
+
+ /*
+ * Sign the message, if flags2 indicates.
+ */
+ if (rqp->rq_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) {
+ smb_rq_sign(rqp);
+ }
+
+ /*
+ * Send it, wait for the reply.
+ */
+ if ((err = smb_ssn_send(ctx, &rqp->rq_rq)) != 0)
+ return (err);
+
+ if ((err = smb_ssn_recv(ctx, &rqp->rq_rp)) != 0)
+ return (err);
+
+ /*
+ * Should have an SMB header, at least.
+ */
+ mbp = &rqp->rq_rp;
+ if (mbp->mb_cur->m_len < SMB_HDRLEN) {
+ DPRINT("len < 32");
+ return (EBADRPC);
+ }
+
+ /*
+ * If the request was signed, validate the
+ * signature on the response.
+ */
+ if (rqp->rq_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) {
+ err = smb_rq_verify(rqp);
+ if (err) {
+ DPRINT("bad signature");
+ return (err);
+ }
+ }
+
+ /*
+ * Decode the SMB header.
+ */
+ mb_get_mem(mbp, (char *)sigbuf, 4);
+ if (0 != bcmp(sigbuf, ffsmb, 4)) {
+ DPRINT("not SMB");
+ return (EBADRPC);
+ }
+ mb_get_uint8(mbp, &ctmp); /* SMB cmd */
+ mb_get_uint32le(mbp, &rqp->rq_status);
+ mb_get_uint8(mbp, &rqp->rq_hflags);
+ mb_get_uint16le(mbp, &rqp->rq_hflags2);
+ mb_get_uint16le(mbp, NULL); /* pid_hi */
+ mb_get_mem(mbp, NULL, 8); /* signature */
+ mb_get_uint16le(mbp, NULL); /* reserved */
+ mb_get_uint16le(mbp, &rqp->rq_tid);
+ mb_get_uint16le(mbp, NULL); /* pid_lo */
+ mb_get_uint16le(mbp, &rqp->rq_uid);
+ mb_get_uint16le(mbp, &rqp->rq_mid);
+
+ /*
+ * Figure out the status return.
+ * Caller looks at rq_status.
+ */
+ if ((rqp->rq_hflags2 & SMB_FLAGS2_ERR_STATUS) == 0) {
+ uint16_t serr;
+ uint8_t class;
+
+ class = rqp->rq_status & 0xff;
+ serr = rqp->rq_status >> 16;
+ rqp->rq_status = smb_map_doserr(class, serr);
+ }
+
+ return (0);
+}
+
+/*
+ * Map old DOS errors (etc.) to NT status codes.
+ * We probably don't need this anymore, since
+ * the oldest server we talk to is NT. But if
+ * later find we do need this, add support here
+ * for the DOS errors we care about.
+ */
+static uint32_t
+smb_map_doserr(uint8_t class, uint16_t serr)
+{
+ if (class == 0 && serr == 0)
+ return (0);
+
+ DPRINT("class 0x%x serr 0x%x", (int)class, (int)serr);
+ return (NT_STATUS_UNSUCCESSFUL);
+}