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