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
smb_rap_parserqparam(const char * s,char ** next,int * rlen)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
smb_rap_parserpparam(const char * s,char ** next,int * rlen)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
smb_rap_parserpdata(const char * s,char ** next,int * rlen)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
smb_rap_rqparam_z(struct smb_rap * rap,const char * value)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
smb_rap_rqparam(struct smb_rap * rap,char ptype,char plen,int value)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
smb_rap_create(int fn,const char * param,const char * data,struct smb_rap ** rapp)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
smb_rap_done(struct smb_rap * rap)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
smb_rap_setNparam(struct smb_rap * rap,int value)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
smb_rap_setPparam(struct smb_rap * rap,void * value)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
smb_rap_getNparam(struct smb_rap * rap,long * value)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
smb_rap_request(struct smb_rap * rap,struct smb_ctx * ctx)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
smb_rap_error(struct smb_rap * rap,int error)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