1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
26 */
27
28 /*
29 * Msgbuf buffer management implementation. The smb_msgbuf interface is
30 * typically used to encode or decode SMB data using sprintf/scanf
31 * style operations. It contains special handling for the SMB header.
32 * It can also be used for general purpose encoding and decoding.
33 */
34
35 #include <sys/types.h>
36 #include <sys/varargs.h>
37 #include <sys/byteorder.h>
38 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
39 #include <stdlib.h>
40 #include <syslog.h>
41 #include <string.h>
42 #include <strings.h>
43 #else
44 #include <sys/sunddi.h>
45 #include <sys/kmem.h>
46 #endif
47 #include <smbsrv/string.h>
48 #include <smbsrv/msgbuf.h>
49 #include <smbsrv/smb.h>
50
51 static int buf_decode(smb_msgbuf_t *, char *, va_list ap);
52 static int buf_encode(smb_msgbuf_t *, char *, va_list ap);
53 static void *smb_msgbuf_malloc(smb_msgbuf_t *, size_t);
54 static int smb_msgbuf_chkerc(char *text, int erc);
55 static void buf_decode_wcs(smb_wchar_t *, smb_wchar_t *, int wcstrlen);
56
57 /*
58 * Returns the offset or number of bytes used within the buffer.
59 */
60 size_t
smb_msgbuf_used(smb_msgbuf_t * mb)61 smb_msgbuf_used(smb_msgbuf_t *mb)
62 {
63 /*LINTED E_PTRDIFF_OVERFLOW*/
64 return (mb->scan - mb->base);
65 }
66
67 /*
68 * Returns the actual buffer size.
69 */
70 size_t
smb_msgbuf_size(smb_msgbuf_t * mb)71 smb_msgbuf_size(smb_msgbuf_t *mb)
72 {
73 return (mb->max);
74 }
75
76 uint8_t *
smb_msgbuf_base(smb_msgbuf_t * mb)77 smb_msgbuf_base(smb_msgbuf_t *mb)
78 {
79 return (mb->base);
80 }
81
82 /*
83 * Ensure that the scan is aligned on a word (16-bit) boundary.
84 */
85 void
smb_msgbuf_word_align(smb_msgbuf_t * mb)86 smb_msgbuf_word_align(smb_msgbuf_t *mb)
87 {
88 mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 1) & ~1);
89 }
90
91 /*
92 * Ensure that the scan is aligned on a dword (32-bit) boundary.
93 */
94 void
smb_msgbuf_dword_align(smb_msgbuf_t * mb)95 smb_msgbuf_dword_align(smb_msgbuf_t *mb)
96 {
97 mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 3) & ~3);
98 }
99
100 /*
101 * Checks whether or not the buffer has space for the amount of data
102 * specified. Returns 1 if there is space, otherwise returns 0.
103 */
104 int
smb_msgbuf_has_space(smb_msgbuf_t * mb,size_t size)105 smb_msgbuf_has_space(smb_msgbuf_t *mb, size_t size)
106 {
107 if (size > mb->max || (mb->scan + size) > mb->end)
108 return (0);
109
110 return (1);
111 }
112
113 /*
114 * Set flags the smb_msgbuf.
115 */
116 void
smb_msgbuf_fset(smb_msgbuf_t * mb,uint32_t flags)117 smb_msgbuf_fset(smb_msgbuf_t *mb, uint32_t flags)
118 {
119 mb->flags |= flags;
120 }
121
122 /*
123 * Clear flags the smb_msgbuf.
124 */
125 void
smb_msgbuf_fclear(smb_msgbuf_t * mb,uint32_t flags)126 smb_msgbuf_fclear(smb_msgbuf_t *mb, uint32_t flags)
127 {
128 mb->flags &= ~flags;
129 }
130
131 /*
132 * smb_msgbuf_init
133 *
134 * Initialize a smb_msgbuf_t structure based on the buffer and size
135 * specified. Both scan and base initially point to the beginning
136 * of the buffer and end points to the limit of the buffer. As
137 * data is added scan should be incremented to point to the next
138 * offset at which data will be written. Max and count are set
139 * to the actual buffer size.
140 */
141 void
smb_msgbuf_init(smb_msgbuf_t * mb,uint8_t * buf,size_t size,uint32_t flags)142 smb_msgbuf_init(smb_msgbuf_t *mb, uint8_t *buf, size_t size, uint32_t flags)
143 {
144 mb->scan = mb->base = buf;
145 mb->max = mb->count = size;
146 mb->end = &buf[size];
147 mb->flags = flags;
148 mb->mlist.next = 0;
149 }
150
151
152 /*
153 * smb_msgbuf_term
154 *
155 * Destruct a smb_msgbuf_t. Free any memory hanging off the mlist.
156 */
157 void
smb_msgbuf_term(smb_msgbuf_t * mb)158 smb_msgbuf_term(smb_msgbuf_t *mb)
159 {
160 smb_msgbuf_mlist_t *item = mb->mlist.next;
161 smb_msgbuf_mlist_t *tmp;
162
163 while (item) {
164 tmp = item;
165 item = item->next;
166 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
167 free(tmp);
168 #else
169 kmem_free(tmp, tmp->size);
170 #endif
171 }
172 }
173
174
175 /*
176 * smb_msgbuf_decode
177 *
178 * Decode a smb_msgbuf buffer as indicated by the format string into
179 * the variable arg list. This is similar to a scanf operation.
180 *
181 * On success, returns the number of bytes encoded. Otherwise
182 * returns a -ve error code.
183 */
184 int
smb_msgbuf_decode(smb_msgbuf_t * mb,char * fmt,...)185 smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...)
186 {
187 int rc;
188 uint8_t *orig_scan;
189 va_list ap;
190
191 va_start(ap, fmt);
192 orig_scan = mb->scan;
193 rc = buf_decode(mb, fmt, ap);
194 va_end(ap);
195
196 if (rc != SMB_MSGBUF_SUCCESS) {
197 (void) smb_msgbuf_chkerc("smb_msgbuf_decode", rc);
198 mb->scan = orig_scan;
199 return (rc);
200 }
201
202 /*LINTED E_PTRDIFF_OVERFLOW*/
203 return (mb->scan - orig_scan);
204 }
205
206
207 /*
208 * buf_decode
209 *
210 * Private decode function, where the real work of decoding the smb_msgbuf
211 * is done. This function should only be called via smb_msgbuf_decode to
212 * ensure correct behaviour and error handling.
213 */
214 static int
buf_decode(smb_msgbuf_t * mb,char * fmt,va_list ap)215 buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap)
216 {
217 uint32_t ival;
218 uint8_t c;
219 uint8_t *cvalp;
220 uint8_t **cvalpp;
221 uint16_t *wvalp;
222 uint32_t *lvalp;
223 uint64_t *llvalp;
224 smb_wchar_t *wcs;
225 int repc;
226 int rc;
227
228 while ((c = *fmt++) != 0) {
229 repc = 1;
230
231 if (c == ' ' || c == '\t')
232 continue;
233
234 if (c == '(') {
235 while (((c = *fmt++) != 0) && c != ')')
236 ;
237
238 if (!c)
239 return (SMB_MSGBUF_SUCCESS);
240
241 continue;
242 }
243
244 if ('0' <= c && c <= '9') {
245 repc = 0;
246 do {
247 repc = repc * 10 + c - '0';
248 c = *fmt++;
249 } while ('0' <= c && c <= '9');
250 } else if (c == '#') {
251 repc = va_arg(ap, int);
252 c = *fmt++;
253 }
254
255 switch (c) {
256 case '.':
257 if (smb_msgbuf_has_space(mb, repc) == 0)
258 return (SMB_MSGBUF_UNDERFLOW);
259
260 mb->scan += repc;
261 break;
262
263 case 'c':
264 if (smb_msgbuf_has_space(mb, repc) == 0)
265 return (SMB_MSGBUF_UNDERFLOW);
266
267 cvalp = va_arg(ap, uint8_t *);
268 bcopy(mb->scan, cvalp, repc);
269 mb->scan += repc;
270 break;
271
272 case 'b':
273 if (smb_msgbuf_has_space(mb, repc) == 0)
274 return (SMB_MSGBUF_UNDERFLOW);
275
276 cvalp = va_arg(ap, uint8_t *);
277 while (repc-- > 0) {
278 *cvalp++ = *mb->scan++;
279 }
280 break;
281
282 case 'w':
283 rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t));
284 if (rc == 0)
285 return (SMB_MSGBUF_UNDERFLOW);
286
287 wvalp = va_arg(ap, uint16_t *);
288 while (repc-- > 0) {
289 *wvalp++ = LE_IN16(mb->scan);
290 mb->scan += sizeof (uint16_t);
291 }
292 break;
293
294 case 'l':
295 rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t));
296 if (rc == 0)
297 return (SMB_MSGBUF_UNDERFLOW);
298
299 lvalp = va_arg(ap, uint32_t *);
300 while (repc-- > 0) {
301 *lvalp++ = LE_IN32(mb->scan);
302 mb->scan += sizeof (int32_t);
303 }
304 break;
305
306 case 'q':
307 rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
308 if (rc == 0)
309 return (SMB_MSGBUF_UNDERFLOW);
310
311 llvalp = va_arg(ap, uint64_t *);
312 while (repc-- > 0) {
313 *llvalp++ = LE_IN64(mb->scan);
314 mb->scan += sizeof (int64_t);
315 }
316 break;
317
318 case 'u': /* Convert from unicode if flags are set */
319 if (mb->flags & SMB_MSGBUF_UNICODE)
320 goto unicode_translation;
321 /*FALLTHROUGH*/
322
323 case 's':
324 ival = strlen((const char *)mb->scan) + 1;
325 if (smb_msgbuf_has_space(mb, ival) == 0)
326 return (SMB_MSGBUF_UNDERFLOW);
327
328 if ((cvalp = smb_msgbuf_malloc(mb, ival * 2)) == 0)
329 return (SMB_MSGBUF_UNDERFLOW);
330
331 if ((ival = smb_stombs((char *)cvalp,
332 (char *)mb->scan, ival * 2)) ==
333 (uint32_t)-1) {
334 return (SMB_MSGBUF_DATA_ERROR);
335 }
336
337 cvalpp = va_arg(ap, uint8_t **);
338 *cvalpp = cvalp;
339 mb->scan += (ival+1);
340 break;
341
342 case 'U': /* Convert from unicode */
343 unicode_translation:
344 /*
345 * Unicode strings are always word aligned.
346 * The malloc'd area is larger than the
347 * original string because the UTF-8 chars
348 * may be longer than the wide-chars.
349 */
350 smb_msgbuf_word_align(mb);
351 /*LINTED E_BAD_PTR_CAST_ALIGN*/
352 wcs = (smb_wchar_t *)mb->scan;
353
354 /* count the null wchar */
355 repc = sizeof (smb_wchar_t);
356 while (*wcs++)
357 repc += sizeof (smb_wchar_t);
358
359 if (smb_msgbuf_has_space(mb, repc) == 0)
360 return (SMB_MSGBUF_UNDERFLOW);
361
362 /* Decode wchar string into host byte-order */
363 if ((wcs = smb_msgbuf_malloc(mb, repc)) == 0)
364 return (SMB_MSGBUF_UNDERFLOW);
365
366 /*LINTED E_BAD_PTR_CAST_ALIGN*/
367 buf_decode_wcs(wcs, (smb_wchar_t *)mb->scan,
368 repc / sizeof (smb_wchar_t));
369
370 /* Get space for translated string */
371 if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0)
372 return (SMB_MSGBUF_UNDERFLOW);
373
374 /* Translate string */
375 (void) smb_wcstombs((char *)cvalp, wcs, repc * 2);
376
377 cvalpp = va_arg(ap, uint8_t **);
378 *cvalpp = cvalp;
379 mb->scan += repc;
380 break;
381
382 case 'M':
383 if (smb_msgbuf_has_space(mb, 4) == 0)
384 return (SMB_MSGBUF_UNDERFLOW);
385
386 if (mb->scan[0] != 0xFF ||
387 mb->scan[1] != 'S' ||
388 mb->scan[2] != 'M' ||
389 mb->scan[3] != 'B') {
390 return (SMB_MSGBUF_INVALID_HEADER);
391 }
392 mb->scan += 4;
393 break;
394
395 default:
396 return (SMB_MSGBUF_INVALID_FORMAT);
397 }
398 }
399
400 return (SMB_MSGBUF_SUCCESS);
401 }
402
403
404 /*
405 * smb_msgbuf_encode
406 *
407 * Encode a smb_msgbuf buffer as indicated by the format string using
408 * the variable arg list. This is similar to a sprintf operation.
409 *
410 * On success, returns the number of bytes encoded. Otherwise
411 * returns a -ve error code.
412 */
413 int
smb_msgbuf_encode(smb_msgbuf_t * mb,char * fmt,...)414 smb_msgbuf_encode(smb_msgbuf_t *mb, char *fmt, ...)
415 {
416 int rc;
417 uint8_t *orig_scan;
418 va_list ap;
419
420 va_start(ap, fmt);
421 orig_scan = mb->scan;
422 rc = buf_encode(mb, fmt, ap);
423 va_end(ap);
424
425 if (rc != SMB_MSGBUF_SUCCESS) {
426 (void) smb_msgbuf_chkerc("smb_msgbuf_encode", rc);
427 mb->scan = orig_scan;
428 return (rc);
429 }
430
431 /*LINTED E_PTRDIFF_OVERFLOW*/
432 return (mb->scan - orig_scan);
433 }
434
435
436 /*
437 * buf_encode
438 *
439 * Private encode function, where the real work of encoding the smb_msgbuf
440 * is done. This function should only be called via smb_msgbuf_encode to
441 * ensure correct behaviour and error handling.
442 */
443 static int
buf_encode(smb_msgbuf_t * mb,char * fmt,va_list ap)444 buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap)
445 {
446 uint8_t cval;
447 uint16_t wval;
448 uint32_t lval;
449 uint64_t llval;
450 uint32_t ival;
451 uint8_t *cvalp;
452 uint8_t c;
453 smb_wchar_t wcval;
454 int count;
455 int repc = 1;
456 int rc;
457
458 while ((c = *fmt++) != 0) {
459 repc = 1;
460
461 if (c == ' ' || c == '\t')
462 continue;
463
464 if (c == '(') {
465 while (((c = *fmt++) != 0) && c != ')')
466 ;
467
468 if (!c)
469 return (SMB_MSGBUF_SUCCESS);
470
471 continue;
472 }
473
474 if ('0' <= c && c <= '9') {
475 repc = 0;
476 do {
477 repc = repc * 10 + c - '0';
478 c = *fmt++;
479 } while ('0' <= c && c <= '9');
480 } else if (c == '#') {
481 repc = va_arg(ap, int);
482 c = *fmt++;
483 }
484
485 switch (c) {
486 case '.':
487 if (smb_msgbuf_has_space(mb, repc) == 0)
488 return (SMB_MSGBUF_OVERFLOW);
489
490 while (repc-- > 0)
491 *mb->scan++ = 0;
492 break;
493
494 case 'c':
495 if (smb_msgbuf_has_space(mb, repc) == 0)
496 return (SMB_MSGBUF_OVERFLOW);
497
498 cvalp = va_arg(ap, uint8_t *);
499 bcopy(cvalp, mb->scan, repc);
500 mb->scan += repc;
501 break;
502
503 case 'b':
504 if (smb_msgbuf_has_space(mb, repc) == 0)
505 return (SMB_MSGBUF_OVERFLOW);
506
507 while (repc-- > 0) {
508 cval = va_arg(ap, int);
509 *mb->scan++ = cval;
510 }
511 break;
512
513 case 'w':
514 rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t));
515 if (rc == 0)
516 return (SMB_MSGBUF_OVERFLOW);
517
518 while (repc-- > 0) {
519 wval = va_arg(ap, int);
520 LE_OUT16(mb->scan, wval);
521 mb->scan += sizeof (uint16_t);
522 }
523 break;
524
525 case 'l':
526 rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t));
527 if (rc == 0)
528 return (SMB_MSGBUF_OVERFLOW);
529
530 while (repc-- > 0) {
531 lval = va_arg(ap, uint32_t);
532 LE_OUT32(mb->scan, lval);
533 mb->scan += sizeof (int32_t);
534 }
535 break;
536
537 case 'q':
538 rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
539 if (rc == 0)
540 return (SMB_MSGBUF_OVERFLOW);
541
542 while (repc-- > 0) {
543 llval = va_arg(ap, uint64_t);
544 LE_OUT64(mb->scan, llval);
545 mb->scan += sizeof (uint64_t);
546 }
547 break;
548
549 case 'u': /* conditional unicode */
550 if (mb->flags & SMB_MSGBUF_UNICODE)
551 goto unicode_translation;
552 /* FALLTHROUGH */
553
554 case 's':
555 cvalp = va_arg(ap, uint8_t *);
556 ival = strlen((const char *)cvalp) + 1;
557
558 if (smb_msgbuf_has_space(mb, ival) == 0)
559 return (SMB_MSGBUF_OVERFLOW);
560
561 ival =
562 smb_mbstos((char *)mb->scan, (const char *)cvalp);
563 mb->scan += ival + 1;
564 break;
565
566 case 'U': /* unicode */
567 unicode_translation:
568 /*
569 * Unicode strings are always word aligned.
570 */
571 smb_msgbuf_word_align(mb);
572 cvalp = va_arg(ap, uint8_t *);
573
574 for (;;) {
575 rc = smb_msgbuf_has_space(mb,
576 sizeof (smb_wchar_t));
577 if (rc == 0)
578 return (SMB_MSGBUF_OVERFLOW);
579
580 count = smb_mbtowc(&wcval, (const char *)cvalp,
581 MTS_MB_CHAR_MAX);
582
583 if (count < 0) {
584 return (SMB_MSGBUF_DATA_ERROR);
585 } else if (count == 0) {
586 /*
587 * No longer need to do this now that
588 * mbtowc correctly writes the null
589 * before returning zero but paranoia
590 * wins.
591 */
592 wcval = 0;
593 count = 1;
594 }
595
596 /* Write wchar in wire-format */
597 LE_OUT16(mb->scan, wcval);
598
599 if (*cvalp == 0) {
600 /*
601 * End of string. Check to see whether
602 * or not to include the null
603 * terminator.
604 */
605 if ((mb->flags & SMB_MSGBUF_NOTERM) ==
606 0)
607 mb->scan +=
608 sizeof (smb_wchar_t);
609 break;
610 }
611
612 mb->scan += sizeof (smb_wchar_t);
613 cvalp += count;
614 }
615 break;
616
617 case 'M':
618 if (smb_msgbuf_has_space(mb, 4) == 0)
619 return (SMB_MSGBUF_OVERFLOW);
620
621 *mb->scan++ = 0xFF;
622 *mb->scan++ = 'S';
623 *mb->scan++ = 'M';
624 *mb->scan++ = 'B';
625 break;
626
627 default:
628 return (SMB_MSGBUF_INVALID_FORMAT);
629 }
630 }
631
632 return (SMB_MSGBUF_SUCCESS);
633 }
634
635
636 /*
637 * smb_msgbuf_malloc
638 *
639 * Allocate some memory for use with this smb_msgbuf. We increase the
640 * requested size to hold the list pointer and return a pointer
641 * to the area for use by the caller.
642 */
643 static void *
smb_msgbuf_malloc(smb_msgbuf_t * mb,size_t size)644 smb_msgbuf_malloc(smb_msgbuf_t *mb, size_t size)
645 {
646 smb_msgbuf_mlist_t *item;
647
648 size += sizeof (smb_msgbuf_mlist_t);
649
650 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
651 if ((item = malloc(size)) == NULL)
652 return (NULL);
653 #else
654 item = kmem_alloc(size, KM_SLEEP);
655 #endif
656 item->next = mb->mlist.next;
657 item->size = size;
658 mb->mlist.next = item;
659
660 /*
661 * The caller gets a pointer to the address
662 * immediately after the smb_msgbuf_mlist_t.
663 */
664 return ((void *)(item + 1));
665 }
666
667
668 /*
669 * smb_msgbuf_chkerc
670 *
671 * Diagnostic function to write an appropriate message to the system log.
672 */
673 static int
smb_msgbuf_chkerc(char * text,int erc)674 smb_msgbuf_chkerc(char *text, int erc)
675 {
676 static struct {
677 int erc;
678 char *name;
679 } etable[] = {
680 { SMB_MSGBUF_SUCCESS, "success" },
681 { SMB_MSGBUF_UNDERFLOW, "overflow/underflow" },
682 { SMB_MSGBUF_INVALID_FORMAT, "invalid format" },
683 { SMB_MSGBUF_INVALID_HEADER, "invalid header" },
684 { SMB_MSGBUF_DATA_ERROR, "data error" }
685 };
686
687 int i;
688
689 for (i = 0; i < sizeof (etable)/sizeof (etable[0]); ++i) {
690 if (etable[i].erc == erc) {
691 if (text == 0)
692 text = "smb_msgbuf_chkerc";
693 break;
694 }
695 }
696 return (erc);
697 }
698
699 static void
buf_decode_wcs(smb_wchar_t * dst_wcstr,smb_wchar_t * src_wcstr,int wcstrlen)700 buf_decode_wcs(smb_wchar_t *dst_wcstr, smb_wchar_t *src_wcstr, int wcstrlen)
701 {
702 int i;
703
704 for (i = 0; i < wcstrlen; i++) {
705 *dst_wcstr = LE_IN16(src_wcstr);
706 dst_wcstr++;
707 src_wcstr++;
708 }
709 }
710