1 /*
   2  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
   3  * Copyright (c) 2000, Boris Popov
   4  * All rights reserved.
   5  *
   6  * Redistribution and use in source and binary forms, with or without
   7  * modification, are permitted provided that the following conditions
   8  * are met:
   9  * 1. Redistributions of source code must retain the above copyright
  10  *    notice, this list of conditions and the following disclaimer.
  11  * 2. Redistributions in binary form must reproduce the above copyright
  12  *    notice, this list of conditions and the following disclaimer in the
  13  *    documentation and/or other materials provided with the distribution.
  14  * 3. All advertising materials mentioning features or use of this software
  15  *    must display the following acknowledgement:
  16  *    This product includes software developed by Boris Popov.
  17  * 4. Neither the name of the author nor the names of any co-contributors
  18  *    may be used to endorse or promote products derived from this software
  19  *    without specific prior written permission.
  20  *
  21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31  * SUCH DAMAGE.
  32  *
  33  * $Id: rap.c,v 1.5 2004/12/13 00:25:23 lindak Exp $
  34  *
  35  * This is very simple implementation of RAP protocol.
  36  */
  37 
  38 #include <sys/param.h>
  39 #include <sys/errno.h>
  40 #include <sys/stat.h>
  41 #include <sys/isa_defs.h>
  42 
  43 #include <ctype.h>
  44 #include <stdio.h>
  45 #include <unistd.h>
  46 #include <strings.h>
  47 #include <stdlib.h>
  48 #include <libintl.h>
  49 #include <sysexits.h>
  50 
  51 #include <netsmb/mchain.h>
  52 #include <netsmb/smb_lib.h>
  53 #include <netsmb/smb_rap.h>
  54 #include "private.h"
  55 
  56 static int
  57 smb_rap_parserqparam(const char *s, char **next, int *rlen)
  58 {
  59         char *np;
  60         int len;
  61 
  62         switch (*s++) {
  63         case 'L':
  64         case 'T':
  65         case 'W':
  66                 len = 2;
  67                 break;
  68         case 'D':
  69         case 'O':
  70                 len = 4;
  71                 break;
  72         case 'b':
  73         case 'F':
  74                 len = 1;
  75                 break;
  76         case 'r':
  77         case 's':
  78                 len = 0;
  79                 break;
  80         default:
  81                 return (EINVAL);
  82         }
  83         if (isdigit(*s)) {
  84                 len *= strtoul(s, &np, 10);
  85                 s = np;
  86         }
  87         *rlen = len;
  88         *(const char **)next = s;
  89         return (0);
  90 }
  91 
  92 static int
  93 smb_rap_parserpparam(const char *s, char **next, int *rlen)
  94 {
  95         char *np;
  96         int len = 0;
  97 
  98         switch (*s++) {
  99         case 'e':
 100         case 'h':
 101                 len = 2;
 102                 break;
 103         case 'i':
 104                 len = 4;
 105                 break;
 106         case 'g':
 107                 len = 1;
 108                 break;
 109         default:
 110                 return (EINVAL);
 111         }
 112         if (isdigit(*s)) {
 113                 len *= strtoul(s, &np, 10);
 114                 s = np;
 115         }
 116         *rlen = len;
 117         *(const char **)next = s;
 118         return (0);
 119 }
 120 
 121 static int
 122 smb_rap_parserpdata(const char *s, char **next, int *rlen)
 123 {
 124         char *np;
 125         int len;
 126 
 127         switch (*s++) {
 128         case 'B':
 129                 len = 1;
 130                 break;
 131         case 'W':
 132                 len = 2;
 133                 break;
 134         case 'D':
 135         case 'O':
 136         case 'z':
 137                 len = 4;
 138                 break;
 139         default:
 140                 return (EINVAL);
 141         }
 142         if (isdigit(*s)) {
 143                 len *= strtoul(s, &np, 10);
 144                 s = np;
 145         }
 146         *rlen = len;
 147         *(const char **)next = s;
 148         return (0);
 149 }
 150 
 151 static int
 152 smb_rap_rqparam_z(struct smb_rap *rap, const char *value)
 153 {
 154         int len = strlen(value) + 1;
 155 
 156         bcopy(value, rap->r_npbuf, len);
 157         rap->r_npbuf += len;
 158         rap->r_plen += len;
 159         return (0);
 160 }
 161 
 162 /*
 163  * Marshal RAP request parameters.
 164  * Note: value is in host order.
 165  */
 166 static int
 167 smb_rap_rqparam(struct smb_rap *rap, char ptype, char plen, int value)
 168 {
 169         int len = 0;
 170         uint_t uv = (uint_t)value;
 171         uint32_t *lp;
 172         uint16_t *sp;
 173         char *p;
 174 
 175         switch (ptype) {
 176         case 'L':
 177         case 'W':
 178                 /* LINTED */
 179                 sp = (uint16_t *)rap->r_npbuf;
 180                 *sp = htoles(uv);
 181                 len = sizeof (*sp);
 182                 break;
 183         case 'D':
 184                 /* LINTED */
 185                 lp = (uint32_t *)rap->r_npbuf;
 186                 *lp = htolel(uv);
 187                 len = sizeof (*lp);
 188                 break;
 189         case 'b':
 190                 p = rap->r_npbuf;
 191                 memset(p, uv, plen);
 192                 len = plen;
 193         default:
 194                 return (EINVAL);
 195         }
 196         rap->r_npbuf += len;
 197         rap->r_plen += len;
 198         return (0);
 199 }
 200 
 201 int
 202 smb_rap_create(int fn, const char *param, const char *data,
 203         struct smb_rap **rapp)
 204 {
 205         struct smb_rap *rap;
 206         char *p;
 207         int plen = 0, len = 0;
 208 
 209         rap = malloc(sizeof (*rap));
 210         if (rap == NULL)
 211                 return (ENOMEM);
 212         bzero(rap, sizeof (*rap));
 213         p = rap->r_sparam = rap->r_nparam = strdup(param);
 214         rap->r_sdata = rap->r_ndata = strdup(data);
 215 
 216         /*
 217          * Calculate length of request parameter block
 218          */
 219         len = 2 + strlen(param) + 1 + strlen(data) + 1;
 220         while (*p) {
 221                 if (smb_rap_parserqparam(p, &p, &plen) != 0)
 222                         break;
 223                 len += plen;
 224         }
 225         rap->r_pbuf = rap->r_npbuf = malloc(len);
 226         if (rap->r_pbuf == NULL)
 227                 return (ENOMEM);
 228         (void) smb_rap_rqparam(rap, 'W', 1, fn);
 229         (void) smb_rap_rqparam_z(rap, rap->r_sparam);
 230         (void) smb_rap_rqparam_z(rap, rap->r_sdata);
 231         *rapp = rap;
 232         return (0);
 233 }
 234 
 235 void
 236 smb_rap_done(struct smb_rap *rap)
 237 {
 238         if (rap->r_sparam)
 239                 free(rap->r_sparam);
 240         if (rap->r_sdata)
 241                 free(rap->r_sdata);
 242         if (rap->r_pbuf)
 243                 free(rap->r_pbuf);
 244 #ifdef NOTYETDEFINED
 245         if (rap->r_npbuf)
 246                 free(rap->r_npbuf);
 247         if (rap->r_dbuf)
 248                 free(rap->r_dbuf);
 249         if (rap->r_rcvbuf)
 250                 free(rap->r_rcvbuf);
 251 #endif
 252         free(rap);
 253 }
 254 
 255 int
 256 smb_rap_setNparam(struct smb_rap *rap, int value)
 257 {
 258         char *p = rap->r_nparam;
 259         char ptype = *p;
 260         int error, plen;
 261 
 262         error = smb_rap_parserqparam(p, &p, &plen);
 263         if (error)
 264                 return (error);
 265         switch (ptype) {
 266         case 'L':
 267                 rap->r_rcvbuflen = value;
 268                 /* FALLTHROUGH */
 269         case 'W':
 270         case 'D':
 271         case 'b':
 272                 error = smb_rap_rqparam(rap, ptype, plen, value);
 273                 break;
 274         default:
 275                 return (EINVAL);
 276         }
 277         rap->r_nparam = p;
 278         return (0);
 279 }
 280 
 281 int
 282 smb_rap_setPparam(struct smb_rap *rap, void *value)
 283 {
 284         char *p = rap->r_nparam;
 285         char ptype = *p;
 286         int error, plen;
 287 
 288         error = smb_rap_parserqparam(p, &p, &plen);
 289         if (error)
 290                 return (error);
 291         switch (ptype) {
 292         case 'r':
 293                 rap->r_rcvbuf = value;
 294                 break;
 295         default:
 296                 return (EINVAL);
 297         }
 298         rap->r_nparam = p;
 299         return (0);
 300 }
 301 
 302 int
 303 smb_rap_getNparam(struct smb_rap *rap, long *value)
 304 {
 305         char *p = rap->r_nparam;
 306         char ptype = *p;
 307         int error, plen;
 308         uint16_t        *te;
 309 
 310         error = smb_rap_parserpparam(p, &p, &plen);
 311         if (error)
 312                 return (error);
 313         switch (ptype) {
 314         case 'h':
 315                 /* LINTED */
 316                 te = (uint16_t *)rap->r_npbuf;
 317                 *value = letohs(*te);
 318                 break;
 319         default:
 320                 return (EINVAL);
 321         }
 322         rap->r_npbuf += plen;
 323         rap->r_nparam = p;
 324         return (0);
 325 }
 326 
 327 int
 328 smb_rap_request(struct smb_rap *rap, struct smb_ctx *ctx)
 329 {
 330         uint16_t *rp, conv, *tmp;
 331         uint32_t *p32;
 332         char *dp, *p = rap->r_nparam;
 333         char ptype;
 334         int error, rdatacnt, rparamcnt, entries, done, dlen, buffer_oflow;
 335 
 336         rdatacnt = rap->r_rcvbuflen;
 337         rparamcnt = rap->r_plen;
 338         error = smb_t2_request(ctx->ct_dev_fd,
 339             0, NULL, "\\PIPE\\LANMAN",
 340             rap->r_plen, rap->r_pbuf,             /* int tparamcnt,void *tparam */
 341             0, NULL,                            /* int tdatacnt, void *tdata */
 342             &rparamcnt, rap->r_pbuf,             /* rparamcnt, void *rparam */
 343             &rdatacnt, rap->r_rcvbuf,            /* int *rdatacnt, void *rdata */
 344             &buffer_oflow);
 345         if (error)
 346                 return (error);
 347 
 348         /* LINTED */
 349         rp = (uint16_t *)rap->r_pbuf;
 350 
 351         /*
 352          * Note: First is a "LanMan API" error code.
 353          * See: usr/src/uts/common/smbsrv/lmerr.h
 354          */
 355         if (rparamcnt < 2)
 356                 return (EBADRPC);
 357         rap->r_result = letohs(*rp);
 358         rp++; rparamcnt -= 2;
 359 
 360         if (rap->r_result != 0) {
 361                 /*
 362                  * Could also return zero and let the caller
 363                  * come get r_result via smb_rap_error(),
 364                  * but in case they dont...
 365                  */
 366                 return (rap->r_result | SMB_RAP_ERROR);
 367         }
 368 
 369         if (rparamcnt < 2)
 370                 return (EBADRPC);
 371         conv = letohs(*rp);
 372         rp++; rparamcnt -= 2;
 373 
 374         rap->r_npbuf = (char *)rp;
 375         rap->r_entries = entries = 0;
 376         /* Save the returned data length */
 377         rap->r_rcvbuflen = rdatacnt;
 378         done = 0;
 379 
 380         while (!done && *p) {
 381                 ptype = *p;
 382                 switch (ptype) {
 383                 case 'e':
 384                         if (rparamcnt < 2)
 385                                 return (EBADRPC);
 386                         /* LINTED */
 387                         tmp = (uint16_t *)rap->r_npbuf;
 388                         rap->r_entries = entries = letohs(*tmp);
 389                         rap->r_npbuf += 2;
 390                         rparamcnt -= 2;
 391                         p++;
 392                         break;
 393                 default:
 394                         done = 1;
 395                 }
 396 #if 0   /* commented out in Darwin. Why? */
 397                 error = smb_rap_parserpparam(p, &p, &plen);
 398                 if (error) {
 399                         smb_error(dgettext(TEXT_DOMAIN,
 400                             "reply parameter mismatch %s"), 0, p);
 401                         return (EBADRPC);
 402                 }
 403 #endif
 404         }
 405         rap->r_nparam = p;
 406         /*
 407          * In general, unpacking entries we may need to relocate
 408          * entries for proper aligning. For now use them as is.
 409          */
 410         dp = rap->r_rcvbuf;
 411         while (entries--) {
 412                 p = rap->r_sdata;
 413                 while (*p) {
 414                         ptype = *p;
 415                         error = smb_rap_parserpdata(p, &p, &dlen);
 416                         if (error) {
 417                                 smb_error(dgettext(TEXT_DOMAIN,
 418                                     "reply data mismatch %s"), 0, p);
 419                                 return (EBADRPC);
 420                         }
 421                         if (rdatacnt < dlen)
 422                                 return (EBADRPC);
 423                         switch (ptype) {
 424                         case 'z':
 425                                 /* LINTED */
 426                                 p32 = (uint32_t *)dp;
 427                                 *p32 = (letohl(*p32) & 0xffff) - conv;
 428                                 break;
 429                         }
 430                         dp += dlen;
 431                         rdatacnt -= dlen;
 432                 }
 433         }
 434         return (error);
 435 }
 436 
 437 int
 438 smb_rap_error(struct smb_rap *rap, int error)
 439 {
 440         if (error)
 441                 return (error);
 442         if (rap->r_result == 0)
 443                 return (0);
 444         return (rap->r_result | SMB_RAP_ERROR);
 445 }