xref: /titanic_44/usr/src/lib/libsmbfs/smb/rap.c (revision bde3d612a7c090234c60e6e4578821237a5db135)
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 }
446