2N/A/*
2N/A * Copyright (c) 2000, Boris Popov
2N/A * All rights reserved.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted provided that the following conditions
2N/A * are met:
2N/A * 1. Redistributions of source code must retain the above copyright
2N/A * notice, this list of conditions and the following disclaimer.
2N/A * 2. Redistributions in binary form must reproduce the above copyright
2N/A * notice, this list of conditions and the following disclaimer in the
2N/A * documentation and/or other materials provided with the distribution.
2N/A * 3. All advertising materials mentioning features or use of this software
2N/A * must display the following acknowledgement:
2N/A * This product includes software developed by Boris Popov.
2N/A * 4. Neither the name of the author nor the names of any co-contributors
2N/A * may be used to endorse or promote products derived from this software
2N/A * without specific prior written permission.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2N/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2N/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2N/A * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2N/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2N/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2N/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2N/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2N/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2N/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2N/A * SUCH DAMAGE.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/param.h>
2N/A#include <sys/ioctl.h>
2N/A#include <sys/errno.h>
2N/A#include <sys/stat.h>
2N/A
2N/A#include <ctype.h>
2N/A#include <errno.h>
2N/A#include <stdio.h>
2N/A#include <unistd.h>
2N/A#include <strings.h>
2N/A#include <stdlib.h>
2N/A#include <sysexits.h>
2N/A#include <libintl.h>
2N/A#include <syslog.h>
2N/A
2N/A#include <smb/smb.h>
2N/A#include "smbfs_lib.h"
2N/A#include "smbfs_private.h"
2N/A
2N/A#define MIN_REPLY_SIZE 4096
2N/A
2N/Astatic uint32_t smbfs_map_doserr(uint8_t, uint16_t);
2N/A
2N/A/*
2N/A * Create and initialize a request structure, for either an
2N/A * "internal" request (one that does not use the driver) or
2N/A * a regular "driver" request, that uses driver ioctls.
2N/A *
2N/A * The two kinds are built a little differently:
2N/A * Driver requests are composed starting with the
2N/A * first word of the "variable word vector" section.
2N/A * The driver prepends the SMB header and word count.
2N/A * The driver also needs an output buffer to receive
2N/A * the response, filled in via copyout in the ioctl.
2N/A *
2N/A * Internal requests are composed entirely in this library.
2N/A * Space for the SMB header is reserved here, and later
2N/A * filled in by smbfs_rq_internal before the send/receive.
2N/A */
2N/Aint
2N/Asmbfs_rq_init(struct smb_ctx *ctx, uchar_t cmd, struct smb_rq **rqpp)
2N/A{
2N/A struct smb_rq *rqp;
2N/A
2N/A if ((rqp = malloc(sizeof (*rqp))) == NULL)
2N/A return (ENOMEM);
2N/A
2N/A bzero(rqp, sizeof (*rqp));
2N/A rqp->rq_cmd = cmd;
2N/A rqp->rq_ctx = ctx;
2N/A
2N/A /*
2N/A * Setup the request buffer.
2N/A * Do the reply buffer later.
2N/A */
2N/A if (smbfs_mb_init(&rqp->rq_rq)) {
2N/A smbfs_rq_done(rqp);
2N/A return (ENOMEM);
2N/A }
2N/A
2N/A /* Space for the SMB header (filled in later) */
2N/A smbfs_mb_put_mem(&rqp->rq_rq, NULL, SMB_HEADER_LEN, MB_MSYSTEM);
2N/A
2N/A /*
2N/A * Copy the ctx flags here, so the caller can
2N/A * update the req flags before the OTW call.
2N/A */
2N/A rqp->rq_hflags = ctx->ct_hflags;
2N/A rqp->rq_hflags2 = ctx->ct_hflags2;
2N/A
2N/A *rqpp = rqp;
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/Asmbfs_rq_done(struct smb_rq *rqp)
2N/A{
2N/A smbfs_mb_done(&rqp->rq_rp);
2N/A smbfs_mb_done(&rqp->rq_rq);
2N/A free(rqp);
2N/A}
2N/A
2N/A/*
2N/A * Reserve space for the word count, which is filled in later by
2N/A * smbfs_rq_wend(). Also initialize the counter that it uses
2N/A * to figure out what value to fill in.
2N/A *
2N/A * Note that the word count happens to be 8-bits,
2N/A * which can lead to confusion.
2N/A */
2N/Avoid
2N/Asmbfs_rq_wstart(struct smb_rq *rqp)
2N/A{
2N/A struct mbdata *mbp = &rqp->rq_rq;
2N/A
2N/A (void) smbfs_mb_fit(mbp, 1, &rqp->rq_wcntp);
2N/A rqp->rq_wcbase = mbp->mb_count;
2N/A}
2N/A
2N/A/*
2N/A * Fill in the word count, in the space reserved by
2N/A * smbfs_rq_wstart().
2N/A */
2N/Avoid
2N/Asmbfs_rq_wend(struct smb_rq *rqp)
2N/A{
2N/A struct mbdata *mbp = &rqp->rq_rq;
2N/A int wcnt;
2N/A
2N/A if (rqp->rq_wcntp == NULL) {
2N/A DPRINT("no wcount ptr\n");
2N/A return;
2N/A }
2N/A wcnt = mbp->mb_count - rqp->rq_wcbase;
2N/A if (wcnt > 0x1ff)
2N/A DPRINT("word count too large (%d)\n", wcnt);
2N/A if (wcnt & 1)
2N/A DPRINT("odd word count\n");
2N/A wcnt >>= 1;
2N/A
2N/A /*
2N/A * Fill in the word count (8-bits).
2N/A * Also store it in the rq, in case
2N/A * we're using the ioctl path.
2N/A */
2N/A *rqp->rq_wcntp = (char)wcnt;
2N/A}
2N/A
2N/A/*
2N/A * Reserve space for the byte count, which is filled in later by
2N/A * smbfs_rq_bend(). Also initialize the counter that it uses
2N/A * to figure out what value to fill in.
2N/A *
2N/A * Note that the byte count happens to be 16-bits,
2N/A * which can lead to confusion.
2N/A */
2N/Avoid
2N/Asmbfs_rq_bstart(struct smb_rq *rqp)
2N/A{
2N/A struct mbdata *mbp = &rqp->rq_rq;
2N/A
2N/A (void) smbfs_mb_fit(mbp, 2, &rqp->rq_bcntp);
2N/A rqp->rq_bcbase = mbp->mb_count;
2N/A}
2N/A
2N/A/*
2N/A * Fill in the byte count, in the space reserved by
2N/A * smbfs_rq_bstart().
2N/A */
2N/Avoid
2N/Asmbfs_rq_bend(struct smb_rq *rqp)
2N/A{
2N/A struct mbdata *mbp = &rqp->rq_rq;
2N/A int bcnt;
2N/A
2N/A if (rqp->rq_bcntp == NULL) {
2N/A DPRINT("no bcount ptr\n");
2N/A return;
2N/A }
2N/A bcnt = mbp->mb_count - rqp->rq_bcbase;
2N/A if (bcnt > 0xffff)
2N/A DPRINT("byte count too large (%d)\n", bcnt);
2N/A /*
2N/A * Fill in the byte count (16-bits).
2N/A * Also store it in the rq, in case
2N/A * we're using the ioctl path.
2N/A *
2N/A * The pointer is char * type due to
2N/A * typical off-by-one alignment.
2N/A */
2N/A rqp->rq_bcntp[0] = bcnt & 0xFF;
2N/A rqp->rq_bcntp[1] = (bcnt >> 8);
2N/A}
2N/A
2N/Aint
2N/Asmbfs_rq_t2_request(int dev_fd, int setupcount, uint16_t *setup,
2N/A const char *name,
2N/A int tparamcnt, void *tparam,
2N/A int tdatacnt, void *tdata,
2N/A int *rparamcnt, void *rparam,
2N/A int *rdatacnt, void *rdata,
2N/A int *buffer_oflow)
2N/A{
2N/A smbioc_t2rq_t *krq;
2N/A int i;
2N/A
2N/A if (setupcount < 0 || setupcount >= SMBIOC_T2RQ_MAXSETUP) {
2N/A syslog(LOG_ERR, "smbfs_rq_t2_request: setup=%d: %s",
2N/A setupcount, strerror(EINVAL));
2N/A return (EINVAL);
2N/A }
2N/A
2N/A if ((krq = malloc(sizeof (smbioc_t2rq_t))) == NULL) {
2N/A syslog(LOG_ERR, "smbfs_rq_t2_request: %s", strerror(errno));
2N/A return (ENOMEM);
2N/A }
2N/A
2N/A bzero(krq, sizeof (*krq));
2N/A
2N/A for (i = 0; i < setupcount; i++)
2N/A krq->ioc_setup[i] = setup[i];
2N/A krq->ioc_setupcnt = setupcount;
2N/A (void) strcpy(krq->ioc_name, name);
2N/A krq->ioc_tparamcnt = tparamcnt;
2N/A krq->ioc_tparam = tparam;
2N/A krq->ioc_tdatacnt = tdatacnt;
2N/A krq->ioc_tdata = tdata;
2N/A
2N/A krq->ioc_rparamcnt = *rparamcnt;
2N/A krq->ioc_rdatacnt = *rdatacnt;
2N/A krq->ioc_rparam = rparam;
2N/A krq->ioc_rdata = rdata;
2N/A
2N/A if (ioctl(dev_fd, SMBIOC_T2RQ, krq) == -1) {
2N/A free(krq);
2N/A syslog(LOG_ERR, "smbfs_rq_t2_request: SMBIOC_T2RQ failed: %s",
2N/A strerror(errno));
2N/A return (errno);
2N/A }
2N/A
2N/A *rparamcnt = krq->ioc_rparamcnt;
2N/A *rdatacnt = krq->ioc_rdatacnt;
2N/A *buffer_oflow = (krq->ioc_rpflags2 & SMB_FLAGS2_NT_STATUS) &&
2N/A (krq->ioc_error == NT_STATUS_BUFFER_OVERFLOW);
2N/A free(krq);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Do an over-the-wire call without using the nsmb driver.
2N/A * This is all "internal" to this library, and used only
2N/A * for connection setup (negotiate protocol, etc.)
2N/A */
2N/Aint
2N/Asmbfs_rq_internal(struct smb_ctx *ctx, struct smb_rq *rqp)
2N/A{
2N/A static const uint8_t ffsmb[SMB_MAGIC_LEN] = SMB_MAGIC_SIG;
2N/A struct smb_iods *is = &ctx->ct_iods;
2N/A uint32_t sigbuf[2];
2N/A struct mbdata mbtmp, *mbp;
2N/A int err, save_mlen;
2N/A uint8_t ctmp;
2N/A
2N/A rqp->rq_uid = is->is_smbuid;
2N/A rqp->rq_tid = SMB_TID_RESERVED;
2N/A rqp->rq_mid = is->is_next_mid++;
2N/A
2N/A /*
2N/A * Fill in the NBT and SMB headers
2N/A * Using mbtmp so we can rewind without
2N/A * affecting the passed request mbdata.
2N/A */
2N/A bcopy(&rqp->rq_rq, &mbtmp, sizeof (mbtmp));
2N/A mbp = &mbtmp;
2N/A mbp->mb_cur = mbp->mb_top;
2N/A mbp->mb_pos = mbp->mb_cur->m_data;
2N/A mbp->mb_count = 0;
2N/A /* Have to save and restore m_len */
2N/A save_mlen = mbp->mb_cur->m_len;
2N/A mbp->mb_cur->m_len = 0;
2N/A
2N/A /*
2N/A * rewind done; fill it in
2N/A */
2N/A smbfs_mb_put_mem(mbp, ffsmb, SMB_MAGIC_LEN, MB_MSYSTEM);
2N/A smbfs_mb_put_uint8(mbp, rqp->rq_cmd);
2N/A smbfs_mb_put_uint32le(mbp, 0); /* status */
2N/A smbfs_mb_put_uint8(mbp, rqp->rq_hflags);
2N/A smbfs_mb_put_uint16le(mbp, rqp->rq_hflags2);
2N/A /* pid_hi(2), signature(8), reserved(2) */
2N/A smbfs_mb_put_mem(mbp, NULL, 12, MB_MZERO);
2N/A smbfs_mb_put_uint16le(mbp, rqp->rq_tid);
2N/A smbfs_mb_put_uint16le(mbp, 0); /* pid_lo */
2N/A smbfs_mb_put_uint16le(mbp, rqp->rq_uid);
2N/A smbfs_mb_put_uint16le(mbp, rqp->rq_mid);
2N/A
2N/A /* Restore original m_len */
2N/A mbp->mb_cur->m_len = save_mlen;
2N/A
2N/A /*
2N/A * Sign the message, if flags2 indicates.
2N/A */
2N/A if (rqp->rq_hflags2 & SMB_FLAGS2_SMB_SECURITY_SIGNATURE)
2N/A smbfs_rq_sign(rqp);
2N/A
2N/A /*
2N/A * Send it, wait for the reply.
2N/A */
2N/A if ((err = smbfs_nb_ssn_send(ctx, &rqp->rq_rq)) != 0)
2N/A return (err);
2N/A
2N/A if ((err = smbfs_nb_ssn_recv(ctx, &rqp->rq_rp)) != 0)
2N/A return (err);
2N/A
2N/A /*
2N/A * Should have an SMB header, at least.
2N/A */
2N/A mbp = &rqp->rq_rp;
2N/A if (mbp->mb_cur->m_len < SMB_HEADER_LEN) {
2N/A DPRINT("len < 32");
2N/A return (EBADRPC);
2N/A }
2N/A
2N/A /*
2N/A * If the request was signed, validate the
2N/A * signature on the response.
2N/A */
2N/A if (rqp->rq_hflags2 & SMB_FLAGS2_SMB_SECURITY_SIGNATURE) {
2N/A err = smbfs_rq_verify(rqp);
2N/A if (err) {
2N/A DPRINT("bad signature");
2N/A return (err);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Decode the SMB header.
2N/A */
2N/A smbfs_md_get_mem(mbp, (char *)sigbuf, 4, MB_MSYSTEM);
2N/A if (0 != bcmp(sigbuf, ffsmb, 4)) {
2N/A DPRINT("not SMB");
2N/A return (EBADRPC);
2N/A }
2N/A smbfs_md_get_uint8(mbp, &ctmp); /* SMB cmd */
2N/A smbfs_md_get_uint32le(mbp, &rqp->rq_status);
2N/A smbfs_md_get_uint8(mbp, &rqp->rq_hflags);
2N/A smbfs_md_get_uint16le(mbp, &rqp->rq_hflags2);
2N/A /* pid_hi(2), signature(8), reserved(2) */
2N/A smbfs_md_get_mem(mbp, NULL, 12, MB_MSYSTEM);
2N/A smbfs_md_get_uint16le(mbp, &rqp->rq_tid);
2N/A smbfs_md_get_uint16le(mbp, NULL); /* pid_lo */
2N/A smbfs_md_get_uint16le(mbp, &rqp->rq_uid);
2N/A smbfs_md_get_uint16le(mbp, &rqp->rq_mid);
2N/A
2N/A /*
2N/A * Figure out the status return.
2N/A * Caller looks at rq_status.
2N/A */
2N/A if ((rqp->rq_hflags2 & SMB_FLAGS2_NT_STATUS) == 0) {
2N/A uint16_t serr;
2N/A uint8_t class;
2N/A
2N/A class = rqp->rq_status & 0xff;
2N/A serr = rqp->rq_status >> 16;
2N/A rqp->rq_status = smbfs_map_doserr(class, serr);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Map old DOS errors (etc.) to NT status codes.
2N/A * We probably don't need this anymore, since
2N/A * the oldest server we talk to is NT. But if
2N/A * later find we do need this, add support here
2N/A * for the DOS errors we care about.
2N/A */
2N/Astatic uint32_t
2N/Asmbfs_map_doserr(uint8_t class, uint16_t serr)
2N/A{
2N/A if (class == 0 && serr == 0)
2N/A return (0);
2N/A
2N/A DPRINT("class 0x%x serr 0x%x", (int)class, (int)serr);
2N/A return (NT_STATUS_UNSUCCESSFUL);
2N/A}