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