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