xref: /titanic_44/usr/src/lib/libsmbfs/smb/rap.c (revision fcf3ce441efd61da9bb2884968af01cb7c1452cc)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
38 
39 #include <sys/param.h>
40 #include <sys/errno.h>
41 #include <sys/stat.h>
42 #include <sys/isa_defs.h>
43 
44 #include <ctype.h>
45 #include <stdio.h>
46 #include <unistd.h>
47 #include <strings.h>
48 #include <stdlib.h>
49 #include <libintl.h>
50 #include <sysexits.h>
51 
52 #include <netsmb/mchain.h>
53 #include <netsmb/smb_lib.h>
54 #include <netsmb/smb_rap.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 	char *p = rap->r_npbuf;
170 	int len = 0;
171 	uint_t uv = (uint_t)value;
172 
173 	switch (ptype) {
174 	case 'L':
175 	case 'W':
176 		/* LINTED */
177 		setwle(p, 0, uv);
178 		len = 2;
179 		break;
180 	case 'D':
181 		/* LINTED */
182 		setdle(p, 0, uv);
183 		len = 4;
184 		break;
185 	case 'b':
186 		memset(p, uv, plen);
187 		len = plen;
188 	default:
189 		return (EINVAL);
190 	}
191 	rap->r_npbuf += len;
192 	rap->r_plen += len;
193 	return (0);
194 }
195 
196 int
197 smb_rap_create(int fn, const char *param, const char *data,
198 	struct smb_rap **rapp)
199 {
200 	struct smb_rap *rap;
201 	char *p;
202 	int plen = 0, len = 0;
203 	int i;
204 
205 	rap = malloc(sizeof (*rap));
206 	if (rap == NULL)
207 		return (ENOMEM);
208 	bzero(rap, sizeof (*rap));
209 	p = rap->r_sparam = rap->r_nparam = strdup(param);
210 	rap->r_sdata = rap->r_ndata = strdup(data);
211 
212 	/*
213 	 * Calculate length of request parameter block
214 	 */
215 	len = 2 + strlen(param) + 1 + strlen(data) + 1;
216 	while (*p) {
217 		if (smb_rap_parserqparam(p, &p, &plen) != 0)
218 			break;
219 		len += plen;
220 	}
221 	rap->r_pbuf = rap->r_npbuf = malloc(len);
222 	smb_rap_rqparam(rap, 'W', 1, fn);
223 	smb_rap_rqparam_z(rap, rap->r_sparam);
224 	smb_rap_rqparam_z(rap, rap->r_sdata);
225 	*rapp = rap;
226 	return (0);
227 }
228 
229 void
230 smb_rap_done(struct smb_rap *rap)
231 {
232 	if (rap->r_sparam)
233 		free(rap->r_sparam);
234 	if (rap->r_sdata)
235 		free(rap->r_sdata);
236 	if (rap->r_pbuf)
237 		free(rap->r_pbuf);
238 #ifdef NOTYETDEFINED
239 	if (rap->r_npbuf)
240 		free(rap->r_npbuf);
241 	if (rap->r_dbuf)
242 		free(rap->r_dbuf);
243 	if (rap->r_rcvbuf)
244 		free(rap->r_rcvbuf);
245 #endif
246 	free(rap);
247 }
248 
249 int
250 smb_rap_setNparam(struct smb_rap *rap, int value)
251 {
252 	char *p = rap->r_nparam;
253 	char ptype = *p;
254 	int error, plen;
255 
256 	error = smb_rap_parserqparam(p, &p, &plen);
257 	if (error)
258 		return (error);
259 	switch (ptype) {
260 	case 'L':
261 		rap->r_rcvbuflen = value;
262 		/* FALLTHROUGH */
263 	case 'W':
264 	case 'D':
265 	case 'b':
266 		error = smb_rap_rqparam(rap, ptype, plen, value);
267 		break;
268 	default:
269 		return (EINVAL);
270 	}
271 	rap->r_nparam = p;
272 	return (0);
273 }
274 
275 int
276 smb_rap_setPparam(struct smb_rap *rap, void *value)
277 {
278 	char *p = rap->r_nparam;
279 	char ptype = *p;
280 	int error, plen;
281 
282 	error = smb_rap_parserqparam(p, &p, &plen);
283 	if (error)
284 		return (error);
285 	switch (ptype) {
286 	case 'r':
287 		rap->r_rcvbuf = value;
288 		break;
289 	default:
290 		return (EINVAL);
291 	}
292 	rap->r_nparam = p;
293 	return (0);
294 }
295 
296 static int
297 smb_rap_getNparam(struct smb_rap *rap, long *value)
298 {
299 	char *p = rap->r_nparam;
300 	char ptype = *p;
301 	int error, plen;
302 	uint16_t	*te;
303 
304 	error = smb_rap_parserpparam(p, &p, &plen);
305 	if (error)
306 		return (error);
307 	switch (ptype) {
308 	case 'h':
309 		/* LINTED */
310 		te = (uint16_t *)rap->r_npbuf;
311 		*value = letohs(*te);
312 		break;
313 	default:
314 		return (EINVAL);
315 	}
316 	rap->r_npbuf += plen;
317 	rap->r_nparam = p;
318 	return (0);
319 }
320 
321 int
322 smb_rap_request(struct smb_rap *rap, struct smb_ctx *ctx)
323 {
324 	uint16_t *rp, conv, *tmp;
325 	uint32_t *p32, ps1;
326 	char *dp, *p = rap->r_nparam;
327 	char ptype;
328 	int error, rdatacnt, rparamcnt, entries, done, dlen, buffer_oflow, i;
329 
330 	rdatacnt = rap->r_rcvbuflen;
331 	rparamcnt = rap->r_plen;
332 	error = smb_t2_request(ctx, 0, NULL, "\\PIPE\\LANMAN",
333 	    rap->r_plen, rap->r_pbuf,		/* int tparamcnt,void *tparam */
334 	    0, NULL,				/* int tdatacnt, void *tdata */
335 	    &rparamcnt, rap->r_pbuf,		/* rparamcnt, void *rparam */
336 	    &rdatacnt, rap->r_rcvbuf,		/* int *rdatacnt, void *rdata */
337 	    &buffer_oflow);
338 	if (error)
339 		return (error);
340 
341 	/* LINTED */
342 	rp = (uint16_t *)rap->r_pbuf;
343 
344 	/*
345 	 * Note: First is a "LanMan API" error code.
346 	 * See: usr/src/uts/common/smbsrv/lmerr.h
347 	 */
348 	if (rparamcnt < 2)
349 		return (EBADRPC);
350 	rap->r_result = letohs(*rp);
351 	rp++; rparamcnt -= 2;
352 
353 	if (rap->r_result != 0) {
354 		/*
355 		 * Could also return zero and let the caller
356 		 * come get r_result via smb_rap_error(),
357 		 * but in case they dont...
358 		 */
359 		return (rap->r_result | SMB_RAP_ERROR);
360 	}
361 
362 	if (rparamcnt < 2)
363 		return (EBADRPC);
364 	conv = letohs(*rp);
365 	rp++; rparamcnt -= 2;
366 
367 	rap->r_npbuf = (char *)rp;
368 	rap->r_entries = entries = 0;
369 	/* Save the returned data length */
370 	rap->r_rcvbuflen = rdatacnt;
371 	done = 0;
372 
373 	while (!done && *p) {
374 		ptype = *p;
375 		switch (ptype) {
376 		case 'e':
377 			if (rparamcnt < 2)
378 				return (EBADRPC);
379 			/* LINTED */
380 			tmp = (uint16_t *)rap->r_npbuf;
381 			rap->r_entries = entries = letohs(*tmp);
382 			rap->r_npbuf += 2;
383 			rparamcnt -= 2;
384 			p++;
385 			break;
386 		default:
387 			done = 1;
388 		}
389 #if 0	/* commented out in Darwin. Why? */
390 		error = smb_rap_parserpparam(p, &p, &plen);
391 		if (error) {
392 			smb_error(dgettext(TEXT_DOMAIN,
393 			    "reply parameter mismatch %s"), 0, p);
394 			return (EBADRPC);
395 		}
396 #endif
397 	}
398 	rap->r_nparam = p;
399 	/*
400 	 * In general, unpacking entries we may need to relocate
401 	 * entries for proper aligning. For now use them as is.
402 	 */
403 	dp = rap->r_rcvbuf;
404 	while (entries--) {
405 		p = rap->r_sdata;
406 		while (*p) {
407 			ptype = *p;
408 			error = smb_rap_parserpdata(p, &p, &dlen);
409 			if (error) {
410 				smb_error(dgettext(TEXT_DOMAIN,
411 				    "reply data mismatch %s"), 0, p);
412 				return (EBADRPC);
413 			}
414 			if (rdatacnt < dlen)
415 				return (EBADRPC);
416 			switch (ptype) {
417 			case 'z':
418 				/* LINTED */
419 				p32 = (uint32_t *)dp;
420 				*p32 = (letohl(*p32) & 0xffff) - conv;
421 				break;
422 			}
423 			dp += dlen;
424 			rdatacnt -= dlen;
425 		}
426 	}
427 	return (error);
428 }
429 
430 int
431 smb_rap_error(struct smb_rap *rap, int error)
432 {
433 	if (error)
434 		return (error);
435 	if (rap->r_result == 0)
436 		return (0);
437 	return (rap->r_result | SMB_RAP_ERROR);
438 }
439 
440 /* todo: move this function to libnetapi */
441 int
442 smb_rap_NetShareEnum(struct smb_ctx *ctx, int sLevel, void *pbBuffer,
443 	int *cbBuffer, int *pcEntriesRead, int *pcTotalAvail)
444 {
445 	struct smb_rap *rap;
446 	long lval = -1;
447 	int error;
448 	char *pass;
449 	int i;
450 
451 	error = smb_rap_create(0, "WrLeh", "B13BWz", &rap);
452 	if (error)
453 		return (error);
454 	smb_rap_setNparam(rap, sLevel);		/* W - sLevel */
455 	smb_rap_setPparam(rap, pbBuffer);	/* r - pbBuffer */
456 	smb_rap_setNparam(rap, *cbBuffer);	/* L - cbBuffer */
457 	error = smb_rap_request(rap, ctx);
458 	if (error == 0) {
459 		*pcEntriesRead = rap->r_entries;
460 		error = smb_rap_getNparam(rap, &lval);
461 		*pcTotalAvail = lval;
462 		/* Copy the data length into the IN/OUT variable. */
463 		*cbBuffer = rap->r_rcvbuflen;
464 	}
465 	error = smb_rap_error(rap, error);
466 	smb_rap_done(rap);
467 	return (error);
468 }
469