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