xref: /titanic_44/usr/src/lib/libsmbfs/smb/rq.c (revision b5a2d8455dfa3190fc977c4bec53e91c99012767)
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: rq.c,v 1.4 2004/12/13 00:25:23 lindak Exp $
33  */
34 
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/ioctl.h>
38 #include <sys/errno.h>
39 #include <sys/stat.h>
40 
41 #include <ctype.h>
42 #include <errno.h>
43 #include <stdio.h>
44 #include <unistd.h>
45 #include <strings.h>
46 #include <stdlib.h>
47 #include <sysexits.h>
48 #include <libintl.h>
49 
50 #include <netsmb/smb.h>
51 #include <netsmb/smb_lib.h>
52 #include "private.h"
53 
54 static uint32_t smb_map_doserr(uint8_t, uint16_t);
55 
56 /*
57  * Create and initialize a request structure, for either an
58  * "internal" request (one that does not use the driver) or
59  * a regular "driver" request, that uses driver ioctls.
60  *
61  * The two kinds are built a little differently:
62  * Driver requests are composed starting with the
63  * first word of the "variable word vector" section.
64  * The driver prepends the SMB header and word count.
65  * The driver also needs an output buffer to receive
66  * the response, filled in via copyout in the ioctl.
67  *
68  * Internal requests are composed entirely in this library.
69  * Space for the SMB header is reserved here, and later
70  * filled in by smb_rq_internal before the send/receive.
71  */
72 int
73 smb_rq_init(struct smb_ctx *ctx, uchar_t cmd, struct smb_rq **rqpp)
74 {
75 	struct smb_rq *rqp;
76 
77 	rqp = malloc(sizeof (*rqp));
78 	if (rqp == NULL)
79 		goto errout;
80 	bzero(rqp, sizeof (*rqp));
81 	rqp->rq_cmd = cmd;
82 	rqp->rq_ctx = ctx;
83 
84 	/*
85 	 * Setup the request buffer.
86 	 * Do the reply buffer later.
87 	 */
88 	if (mb_init(&rqp->rq_rq, M_MINSIZE))
89 		goto errout;
90 
91 	/* Space for the SMB header. (filled in later) */
92 	mb_put_mem(&rqp->rq_rq, NULL, SMB_HDRLEN);
93 
94 	/*
95 	 * Copy the ctx flags here, so the caller can
96 	 * update the req flags before the OTW call.
97 	 */
98 	rqp->rq_hflags = ctx->ct_hflags;
99 	rqp->rq_hflags2 = ctx->ct_hflags2;
100 
101 	*rqpp = rqp;
102 	return (0);
103 
104 errout:
105 	if (rqp) {
106 		smb_rq_done(rqp);
107 		free(rqp);
108 	}
109 	return (ENOMEM);
110 }
111 
112 void
113 smb_rq_done(struct smb_rq *rqp)
114 {
115 	mb_done(&rqp->rq_rp);
116 	mb_done(&rqp->rq_rq);
117 	free(rqp);
118 }
119 
120 /*
121  * Reserve space for the word count, which is filled in later by
122  * smb_rq_wend().  Also initialize the counter that it uses
123  * to figure out what value to fill in.
124  *
125  * Note that the word count happens to be 8-bits,
126  * which can lead to confusion.
127  */
128 void
129 smb_rq_wstart(struct smb_rq *rqp)
130 {
131 	struct mbdata *mbp = &rqp->rq_rq;
132 
133 	mb_fit(mbp, 1, &rqp->rq_wcntp);
134 	rqp->rq_wcbase = mbp->mb_count;
135 }
136 
137 /*
138  * Fill in the word count, in the space reserved by
139  * smb_rq_wstart().
140  */
141 void
142 smb_rq_wend(struct smb_rq *rqp)
143 {
144 	struct mbdata *mbp = &rqp->rq_rq;
145 	int wcnt;
146 
147 	if (rqp->rq_wcntp == NULL) {
148 		DPRINT("no wcount ptr\n");
149 		return;
150 	}
151 	wcnt = mbp->mb_count - rqp->rq_wcbase;
152 	if (wcnt > 0x1ff)
153 		DPRINT("word count too large (%d)\n", wcnt);
154 	if (wcnt & 1)
155 		DPRINT("odd word count\n");
156 	wcnt >>= 1;
157 
158 	/*
159 	 * Fill in the word count (8-bits).
160 	 * Also store it in the rq, in case
161 	 * we're using the ioctl path.
162 	 */
163 	*rqp->rq_wcntp = (char)wcnt;
164 }
165 
166 /*
167  * Reserve space for the byte count, which is filled in later by
168  * smb_rq_bend().  Also initialize the counter that it uses
169  * to figure out what value to fill in.
170  *
171  * Note that the byte count happens to be 16-bits,
172  * which can lead to confusion.
173  */
174 void
175 smb_rq_bstart(struct smb_rq *rqp)
176 {
177 	struct mbdata *mbp = &rqp->rq_rq;
178 
179 	mb_fit(mbp, 2, &rqp->rq_bcntp);
180 	rqp->rq_bcbase = mbp->mb_count;
181 }
182 
183 /*
184  * Fill in the byte count, in the space reserved by
185  * smb_rq_bstart().
186  */
187 void
188 smb_rq_bend(struct smb_rq *rqp)
189 {
190 	struct mbdata *mbp = &rqp->rq_rq;
191 	int bcnt;
192 
193 	if (rqp->rq_bcntp == NULL) {
194 		DPRINT("no bcount ptr\n");
195 		return;
196 	}
197 	bcnt = mbp->mb_count - rqp->rq_bcbase;
198 	if (bcnt > 0xffff)
199 		DPRINT("byte count too large (%d)\n", bcnt);
200 	/*
201 	 * Fill in the byte count (16-bits).
202 	 * Also store it in the rq, in case
203 	 * we're using the ioctl path.
204 	 *
205 	 * The pointer is char * type due to
206 	 * typical off-by-one alignment.
207 	 */
208 	rqp->rq_bcntp[0] = bcnt & 0xFF;
209 	rqp->rq_bcntp[1] = (bcnt >> 8);
210 }
211 
212 /*
213  * Removed: smb_rq_dmem
214  * which was mostly like: mb_put_mem
215  */
216 
217 int
218 smb_rq_simple(struct smb_rq *rqp)
219 {
220 	struct smbioc_rq krq;
221 	struct mbdata *mbp;
222 	char *data;
223 	uint32_t len;
224 	size_t rpbufsz;
225 
226 	bzero(&krq, sizeof (krq));
227 	krq.ioc_cmd = rqp->rq_cmd;
228 
229 	/*
230 	 * Make the SMB request body contiguous,
231 	 * and fill in the ioctl request.
232 	 */
233 	mbp = smb_rq_getrequest(rqp);
234 	m_lineup(mbp->mb_top, &mbp->mb_top);
235 	data = mtod(mbp->mb_top, char *);
236 	len = m_totlen(mbp->mb_top);
237 
238 	/*
239 	 * _rq_init left space for the SMB header,
240 	 * which makes mb_count the offset from
241 	 * the beginning of the header (useful).
242 	 * However, in this code path the driver
243 	 * prepends the header, so we skip it.
244 	 */
245 	krq.ioc_tbufsz = len - SMB_HDRLEN;
246 	krq.ioc_tbuf  = data + SMB_HDRLEN;
247 
248 	/*
249 	 * Setup a buffer to hold the reply.
250 	 *
251 	 * Default size is M_MINSIZE, but the
252 	 * caller may increase rq_rpbufsz
253 	 * before calling this.
254 	 */
255 	mbp = smb_rq_getreply(rqp);
256 	rpbufsz = rqp->rq_rpbufsz;
257 	if (rpbufsz < M_MINSIZE)
258 		rpbufsz = M_MINSIZE;
259 	if (mb_init(mbp, rpbufsz))
260 		return (ENOMEM);
261 	krq.ioc_rbufsz = rpbufsz;
262 	krq.ioc_rbuf = mtod(mbp->mb_top, char *);
263 
264 	/*
265 	 * Call the driver
266 	 */
267 	if (ioctl(rqp->rq_ctx->ct_dev_fd, SMBIOC_REQUEST, &krq) == -1)
268 		return (errno);
269 
270 	/*
271 	 * Initialize returned mbdata.
272 	 * SMB header already parsed.
273 	 */
274 	mbp->mb_top->m_len = krq.ioc_rbufsz;
275 
276 	return (0);
277 }
278 
279 
280 int
281 smb_t2_request(struct smb_ctx *ctx, int setupcount, uint16_t *setup,
282 	const char *name,
283 	int tparamcnt, void *tparam,
284 	int tdatacnt, void *tdata,
285 	int *rparamcnt, void *rparam,
286 	int *rdatacnt, void *rdata,
287 	int *buffer_oflow)
288 {
289 	smbioc_t2rq_t *krq;
290 	int i;
291 
292 	krq = (smbioc_t2rq_t *)malloc(sizeof (smbioc_t2rq_t));
293 	bzero(krq, sizeof (*krq));
294 
295 	if (setupcount < 0 || setupcount >= SMBIOC_T2RQ_MAXSETUP) {
296 		/* Bogus setup count, or too many setup words */
297 		return (EINVAL);
298 	}
299 	for (i = 0; i < setupcount; i++)
300 		krq->ioc_setup[i] = setup[i];
301 	krq->ioc_setupcnt = setupcount;
302 	strcpy(krq->ioc_name, name);
303 	krq->ioc_tparamcnt = tparamcnt;
304 	krq->ioc_tparam = tparam;
305 	krq->ioc_tdatacnt = tdatacnt;
306 	krq->ioc_tdata = tdata;
307 
308 	krq->ioc_rparamcnt = *rparamcnt;
309 	krq->ioc_rdatacnt = *rdatacnt;
310 	krq->ioc_rparam = rparam;
311 	krq->ioc_rdata  = rdata;
312 
313 	if (ioctl(ctx->ct_dev_fd, SMBIOC_T2RQ, krq) == -1) {
314 		return (errno);
315 	}
316 
317 	*rparamcnt = krq->ioc_rparamcnt;
318 	*rdatacnt = krq->ioc_rdatacnt;
319 	*buffer_oflow = (krq->ioc_rpflags2 & SMB_FLAGS2_ERR_STATUS) &&
320 	    (krq->ioc_error == NT_STATUS_BUFFER_OVERFLOW);
321 	free(krq);
322 
323 	return (0);
324 }
325 
326 
327 /*
328  * Do an over-the-wire call without using the nsmb driver.
329  * This is all "internal" to this library, and used only
330  * for connection setup (negotiate protocol, etc.)
331  */
332 int
333 smb_rq_internal(struct smb_ctx *ctx, struct smb_rq *rqp)
334 {
335 	static const uint8_t ffsmb[4] = SMB_SIGNATURE;
336 	struct smb_iods *is = &ctx->ct_iods;
337 	uint32_t sigbuf[2];
338 	struct mbdata mbtmp, *mbp;
339 	int err, save_mlen;
340 	uint8_t ctmp;
341 
342 	rqp->rq_uid = is->is_smbuid;
343 	rqp->rq_tid = SMB_TID_UNKNOWN;
344 	rqp->rq_mid = is->is_next_mid++;
345 
346 	/*
347 	 * Fill in the NBT and SMB headers
348 	 * Using mbtmp so we can rewind without
349 	 * affecting the passed request mbdata.
350 	 */
351 	bcopy(&rqp->rq_rq, &mbtmp, sizeof (mbtmp));
352 	mbp = &mbtmp;
353 	mbp->mb_cur = mbp->mb_top;
354 	mbp->mb_pos = mbp->mb_cur->m_data;
355 	mbp->mb_count = 0;
356 	/* Have to save and restore m_len */
357 	save_mlen = mbp->mb_cur->m_len;
358 	mbp->mb_cur->m_len = 0;
359 
360 	/*
361 	 * rewind done; fill it in
362 	 */
363 	mb_put_mem(mbp, (char *)SMB_SIGNATURE, SMB_SIGLEN);
364 	mb_put_uint8(mbp, rqp->rq_cmd);
365 	mb_put_mem(mbp, NULL, 4);	/* status */
366 	mb_put_uint8(mbp, rqp->rq_hflags);
367 	mb_put_uint16le(mbp, rqp->rq_hflags2);
368 	mb_put_uint16le(mbp, 0);	/* pid_hi */
369 	mb_put_mem(mbp, NULL, 8);	/* signature */
370 	mb_put_uint16le(mbp, 0);	/* reserved */
371 	mb_put_uint16le(mbp, rqp->rq_tid);
372 	mb_put_uint16le(mbp, 0);	/* pid_lo */
373 	mb_put_uint16le(mbp, rqp->rq_uid);
374 	mb_put_uint16le(mbp, rqp->rq_mid);
375 
376 	/* Restore original m_len */
377 	mbp->mb_cur->m_len = save_mlen;
378 
379 	/*
380 	 * Sign the message, if flags2 indicates.
381 	 */
382 	if (rqp->rq_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) {
383 		smb_rq_sign(rqp);
384 	}
385 
386 	/*
387 	 * Send it, wait for the reply.
388 	 */
389 	if ((err = smb_ssn_send(ctx, &rqp->rq_rq)) != 0)
390 		return (err);
391 
392 	if ((err = smb_ssn_recv(ctx, &rqp->rq_rp)) != 0)
393 		return (err);
394 
395 	/*
396 	 * Should have an SMB header, at least.
397 	 */
398 	mbp = &rqp->rq_rp;
399 	if (mbp->mb_cur->m_len < SMB_HDRLEN) {
400 		DPRINT("len < 32");
401 		return (EBADRPC);
402 	}
403 
404 	/*
405 	 * If the request was signed, validate the
406 	 * signature on the response.
407 	 */
408 	if (rqp->rq_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) {
409 		err = smb_rq_verify(rqp);
410 		if (err) {
411 			DPRINT("bad signature");
412 			return (err);
413 		}
414 	}
415 
416 	/*
417 	 * Decode the SMB header.
418 	 */
419 	mb_get_mem(mbp, (char *)sigbuf, 4);
420 	if (0 != bcmp(sigbuf, ffsmb, 4)) {
421 		DPRINT("not SMB");
422 		return (EBADRPC);
423 	}
424 	mb_get_uint8(mbp, &ctmp);	/* SMB cmd */
425 	mb_get_uint32le(mbp, &rqp->rq_status);
426 	mb_get_uint8(mbp, &rqp->rq_hflags);
427 	mb_get_uint16le(mbp, &rqp->rq_hflags2);
428 	mb_get_uint16le(mbp, NULL);	/* pid_hi */
429 	mb_get_mem(mbp, NULL, 8); /* signature */
430 	mb_get_uint16le(mbp, NULL);	/* reserved */
431 	mb_get_uint16le(mbp, &rqp->rq_tid);
432 	mb_get_uint16le(mbp, NULL);	/* pid_lo */
433 	mb_get_uint16le(mbp, &rqp->rq_uid);
434 	mb_get_uint16le(mbp, &rqp->rq_mid);
435 
436 	/*
437 	 * Figure out the status return.
438 	 * Caller looks at rq_status.
439 	 */
440 	if ((rqp->rq_hflags2 & SMB_FLAGS2_ERR_STATUS) == 0) {
441 		uint16_t	serr;
442 		uint8_t		class;
443 
444 		class = rqp->rq_status & 0xff;
445 		serr  = rqp->rq_status >> 16;
446 		rqp->rq_status = smb_map_doserr(class, serr);
447 	}
448 
449 	return (0);
450 }
451 
452 /*
453  * Map old DOS errors (etc.) to NT status codes.
454  * We probably don't need this anymore, since
455  * the oldest server we talk to is NT.  But if
456  * later find we do need this, add support here
457  * for the DOS errors we care about.
458  */
459 static uint32_t
460 smb_map_doserr(uint8_t class, uint16_t serr)
461 {
462 	if (class == 0 && serr == 0)
463 		return (0);
464 
465 	DPRINT("class 0x%x serr 0x%x", (int)class, (int)serr);
466 	return (NT_STATUS_UNSUCCESSFUL);
467 }
468