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
56 /*
57 * Returns the offset or number of bytes used within the buffer.
58 */
59 size_t
60 smb_msgbuf_used(smb_msgbuf_t *mb)
61 {
62 /*LINTED E_PTRDIFF_OVERFLOW*/
63 return (mb->scan - mb->base);
64 }
65
66 /*
67 * Returns the actual buffer size.
68 */
69 size_t
70 smb_msgbuf_size(smb_msgbuf_t *mb)
71 {
72 return (mb->max);
73 }
74
75 uint8_t *
160 smb_msgbuf_mlist_t *tmp;
161
162 while (item) {
163 tmp = item;
164 item = item->next;
165 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
166 free(tmp);
167 #else
168 kmem_free(tmp, tmp->size);
169 #endif
170 }
171 }
172
173
174 /*
175 * smb_msgbuf_decode
176 *
177 * Decode a smb_msgbuf buffer as indicated by the format string into
178 * the variable arg list. This is similar to a scanf operation.
179 *
180 * On success, returns the number of bytes encoded. Otherwise
181 * returns a -ve error code.
182 */
183 int
184 smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...)
185 {
186 int rc;
187 uint8_t *orig_scan;
188 va_list ap;
189
190 va_start(ap, fmt);
191 orig_scan = mb->scan;
192 rc = buf_decode(mb, fmt, ap);
193 va_end(ap);
194
195 if (rc != SMB_MSGBUF_SUCCESS) {
196 (void) smb_msgbuf_chkerc("smb_msgbuf_decode", rc);
197 mb->scan = orig_scan;
198 return (rc);
199 }
200
201 /*LINTED E_PTRDIFF_OVERFLOW*/
202 return (mb->scan - orig_scan);
203 }
204
205
206 /*
207 * buf_decode
208 *
209 * Private decode function, where the real work of decoding the smb_msgbuf
210 * is done. This function should only be called via smb_msgbuf_decode to
211 * ensure correct behaviour and error handling.
212 */
213 static int
214 buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap)
215 {
216 uint32_t ival;
217 uint8_t c;
218 uint8_t *bvalp;
219 uint16_t *wvalp;
220 uint32_t *lvalp;
221 uint64_t *llvalp;
222 char *cvalp;
223 char **cvalpp;
224 smb_wchar_t wchar;
225 boolean_t repc_specified;
226 int repc;
227 int rc;
228
229 while ((c = *fmt++) != 0) {
230 repc_specified = B_FALSE;
231 repc = 1;
232
233 if (c == ' ' || c == '\t')
234 continue;
235
236 if (c == '(') {
237 while (((c = *fmt++) != 0) && c != ')')
238 ;
239
240 if (!c)
241 return (SMB_MSGBUF_SUCCESS);
242
243 continue;
244 }
307 }
308 break;
309
310 case 'q': /* get quad */
311 rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
312 if (rc == 0)
313 return (SMB_MSGBUF_UNDERFLOW);
314
315 llvalp = va_arg(ap, uint64_t *);
316 while (repc-- > 0) {
317 *llvalp++ = LE_IN64(mb->scan);
318 mb->scan += sizeof (int64_t);
319 }
320 break;
321
322 case 'u': /* Convert from unicode if flags are set */
323 if (mb->flags & SMB_MSGBUF_UNICODE)
324 goto unicode_translation;
325 /*FALLTHROUGH*/
326
327 case 's': /* get string */
328 if (!repc_specified)
329 repc = strlen((const char *)mb->scan) + 1;
330 if (smb_msgbuf_has_space(mb, repc) == 0)
331 return (SMB_MSGBUF_UNDERFLOW);
332 if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0)
333 return (SMB_MSGBUF_UNDERFLOW);
334 cvalpp = va_arg(ap, char **);
335 *cvalpp = cvalp;
336 /* Translate OEM to mbs */
337 while (repc > 0) {
338 wchar = *mb->scan++;
339 repc--;
340 if (wchar == 0)
341 break;
342 ival = smb_wctomb(cvalp, wchar);
343 cvalp += ival;
344 }
345 *cvalp = '\0';
346 if (repc > 0)
347 mb->scan += repc;
348 break;
349
350 case 'U': /* get unicode string */
351 unicode_translation:
352 /*
353 * Unicode strings are always word aligned.
354 * The malloc'd area is larger than the
355 * original string because the UTF-8 chars
356 * may be longer than the wide-chars.
357 */
358 smb_msgbuf_word_align(mb);
359 if (!repc_specified) {
360 /*
361 * Count bytes, including the null.
362 */
363 uint8_t *tmp_scan = mb->scan;
364 repc = 2; /* the null */
365 while ((wchar = LE_IN16(tmp_scan)) != 0) {
366 tmp_scan += 2;
367 repc += 2;
368 }
369 }
370 if (smb_msgbuf_has_space(mb, repc) == 0)
371 return (SMB_MSGBUF_UNDERFLOW);
372 /*
373 * Get space for translated string
374 * Allocates worst-case size.
375 */
376 if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0)
377 return (SMB_MSGBUF_UNDERFLOW);
378 cvalpp = va_arg(ap, char **);
379 *cvalpp = cvalp;
380 /*
381 * Translate unicode to mbs, stopping after
382 * null or repc limit.
383 */
384 while (repc >= 2) {
385 wchar = LE_IN16(mb->scan);
386 mb->scan += 2;
387 repc -= 2;
388 if (wchar == 0)
389 break;
390 ival = smb_wctomb(cvalp, wchar);
391 cvalp += ival;
392 }
393 *cvalp = '\0';
394 if (repc > 0)
395 mb->scan += repc;
396 break;
397
398 case 'M':
399 if (smb_msgbuf_has_space(mb, 4) == 0)
400 return (SMB_MSGBUF_UNDERFLOW);
401
402 if (mb->scan[0] != 0xFF ||
403 mb->scan[1] != 'S' ||
404 mb->scan[2] != 'M' ||
405 mb->scan[3] != 'B') {
406 return (SMB_MSGBUF_INVALID_HEADER);
407 }
408 mb->scan += 4;
409 break;
410
411 default:
412 return (SMB_MSGBUF_INVALID_FORMAT);
413 }
414 }
415
416 return (SMB_MSGBUF_SUCCESS);
417 }
418
419
420 /*
421 * smb_msgbuf_encode
422 *
423 * Encode a smb_msgbuf buffer as indicated by the format string using
424 * the variable arg list. This is similar to a sprintf operation.
425 *
426 * On success, returns the number of bytes encoded. Otherwise
427 * returns a -ve error code.
428 */
429 int
430 smb_msgbuf_encode(smb_msgbuf_t *mb, char *fmt, ...)
431 {
432 int rc;
433 uint8_t *orig_scan;
434 va_list ap;
435
436 va_start(ap, fmt);
437 orig_scan = mb->scan;
438 rc = buf_encode(mb, fmt, ap);
439 va_end(ap);
440
449 }
450
451
452 /*
453 * buf_encode
454 *
455 * Private encode function, where the real work of encoding the smb_msgbuf
456 * is done. This function should only be called via smb_msgbuf_encode to
457 * ensure correct behaviour and error handling.
458 */
459 static int
460 buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap)
461 {
462 uint8_t cval;
463 uint16_t wval;
464 uint32_t lval;
465 uint64_t llval;
466 uint8_t *bvalp;
467 char *cvalp;
468 uint8_t c;
469 smb_wchar_t wchar;
470 int count;
471 boolean_t repc_specified;
472 int repc;
473 int rc;
474
475 while ((c = *fmt++) != 0) {
476 repc_specified = B_FALSE;
477 repc = 1;
478
479 if (c == ' ' || c == '\t')
480 continue;
481
482 if (c == '(') {
483 while (((c = *fmt++) != 0) && c != ')')
484 ;
485
486 if (!c)
487 return (SMB_MSGBUF_SUCCESS);
488
489 continue;
490 }
554 }
555 break;
556
557 case 'q': /* put quad */
558 rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
559 if (rc == 0)
560 return (SMB_MSGBUF_OVERFLOW);
561
562 while (repc-- > 0) {
563 llval = va_arg(ap, uint64_t);
564 LE_OUT64(mb->scan, llval);
565 mb->scan += sizeof (uint64_t);
566 }
567 break;
568
569 case 'u': /* conditional unicode */
570 if (mb->flags & SMB_MSGBUF_UNICODE)
571 goto unicode_translation;
572 /* FALLTHROUGH */
573
574 case 's': /* put string */
575 cvalp = va_arg(ap, char *);
576 if (!repc_specified) {
577 repc = smb_sbequiv_strlen(cvalp);
578 if (repc == -1)
579 return (SMB_MSGBUF_OVERFLOW);
580 if (!(mb->flags & SMB_MSGBUF_NOTERM))
581 repc++;
582 }
583 if (smb_msgbuf_has_space(mb, repc) == 0)
584 return (SMB_MSGBUF_OVERFLOW);
585 while (repc > 0) {
586 count = smb_mbtowc(&wchar, cvalp,
587 MTS_MB_CHAR_MAX);
588 if (count < 0)
589 return (SMB_MSGBUF_DATA_ERROR);
590 cvalp += count;
591 if (wchar == 0)
592 break;
593 *mb->scan++ = (uint8_t)wchar;
594 repc--;
595 if (wchar & 0xff00) {
596 *mb->scan++ = wchar >> 8;
597 repc--;
598 }
599 }
600 if (*cvalp == '\0' && repc > 0 &&
601 (mb->flags & SMB_MSGBUF_NOTERM) == 0) {
602 *mb->scan++ = 0;
603 repc--;
604 }
605 while (repc > 0) {
606 *mb->scan++ = 0;
607 repc--;
608 }
609 break;
610
611 case 'U': /* put unicode string */
612 unicode_translation:
613 /*
614 * Unicode strings are always word aligned.
615 */
616 smb_msgbuf_word_align(mb);
617 cvalp = va_arg(ap, char *);
618 if (!repc_specified) {
619 repc = smb_wcequiv_strlen(cvalp);
620 if (!(mb->flags & SMB_MSGBUF_NOTERM))
621 repc += 2;
622 }
623 if (!smb_msgbuf_has_space(mb, repc))
624 return (SMB_MSGBUF_OVERFLOW);
625 while (repc >= 2) {
626 count = smb_mbtowc(&wchar, cvalp,
627 MTS_MB_CHAR_MAX);
628 if (count < 0)
629 return (SMB_MSGBUF_DATA_ERROR);
630 cvalp += count;
631 if (wchar == 0)
632 break;
633
634 LE_OUT16(mb->scan, wchar);
635 mb->scan += 2;
636 repc -= 2;
637 }
638 if (*cvalp == '\0' && repc >= 2 &&
639 (mb->flags & SMB_MSGBUF_NOTERM) == 0) {
640 LE_OUT16(mb->scan, 0);
641 mb->scan += 2;
642 repc -= 2;
643 }
644 while (repc > 0) {
645 *mb->scan++ = 0;
646 repc--;
647 }
648 break;
649
650 case 'M':
651 if (smb_msgbuf_has_space(mb, 4) == 0)
652 return (SMB_MSGBUF_OVERFLOW);
653
654 *mb->scan++ = 0xFF;
655 *mb->scan++ = 'S';
656 *mb->scan++ = 'M';
657 *mb->scan++ = 'B';
658 break;
659
660 default:
661 return (SMB_MSGBUF_INVALID_FORMAT);
662 }
663 }
664
665 return (SMB_MSGBUF_SUCCESS);
666 }
667
668
669 /*
670 * smb_msgbuf_malloc
671 *
672 * Allocate some memory for use with this smb_msgbuf. We increase the
673 * requested size to hold the list pointer and return a pointer
674 * to the area for use by the caller.
675 */
676 static void *
677 smb_msgbuf_malloc(smb_msgbuf_t *mb, size_t size)
678 {
679 smb_msgbuf_mlist_t *item;
680
681 size += sizeof (smb_msgbuf_mlist_t);
682
683 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
684 if ((item = malloc(size)) == NULL)
685 return (NULL);
686 #else
687 item = kmem_alloc(size, KM_SLEEP);
688 #endif
689 item->next = mb->mlist.next;
|
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 2018 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
56 static int msgbuf_get_oem_string(smb_msgbuf_t *, char **, int);
57 static int msgbuf_get_unicode_string(smb_msgbuf_t *, char **, int);
58 static int msgbuf_put_oem_string(smb_msgbuf_t *, char *, int);
59 static int msgbuf_put_unicode_string(smb_msgbuf_t *, char *, int);
60
61
62 /*
63 * Returns the offset or number of bytes used within the buffer.
64 */
65 size_t
66 smb_msgbuf_used(smb_msgbuf_t *mb)
67 {
68 /*LINTED E_PTRDIFF_OVERFLOW*/
69 return (mb->scan - mb->base);
70 }
71
72 /*
73 * Returns the actual buffer size.
74 */
75 size_t
76 smb_msgbuf_size(smb_msgbuf_t *mb)
77 {
78 return (mb->max);
79 }
80
81 uint8_t *
166 smb_msgbuf_mlist_t *tmp;
167
168 while (item) {
169 tmp = item;
170 item = item->next;
171 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
172 free(tmp);
173 #else
174 kmem_free(tmp, tmp->size);
175 #endif
176 }
177 }
178
179
180 /*
181 * smb_msgbuf_decode
182 *
183 * Decode a smb_msgbuf buffer as indicated by the format string into
184 * the variable arg list. This is similar to a scanf operation.
185 *
186 * On success, returns the number of bytes decoded. Otherwise
187 * returns a -ve error code.
188 */
189 int
190 smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...)
191 {
192 int rc;
193 uint8_t *orig_scan;
194 va_list ap;
195
196 va_start(ap, fmt);
197 orig_scan = mb->scan;
198 rc = buf_decode(mb, fmt, ap);
199 va_end(ap);
200
201 if (rc != SMB_MSGBUF_SUCCESS) {
202 (void) smb_msgbuf_chkerc("smb_msgbuf_decode", rc);
203 mb->scan = orig_scan;
204 return (rc);
205 }
206
207 /*LINTED E_PTRDIFF_OVERFLOW*/
208 return (mb->scan - orig_scan);
209 }
210
211
212 /*
213 * buf_decode
214 *
215 * Private decode function, where the real work of decoding the smb_msgbuf
216 * is done. This function should only be called via smb_msgbuf_decode to
217 * ensure correct behaviour and error handling.
218 */
219 static int
220 buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap)
221 {
222 uint8_t c;
223 uint8_t *bvalp;
224 uint16_t *wvalp;
225 uint32_t *lvalp;
226 uint64_t *llvalp;
227 char **cvalpp;
228 boolean_t repc_specified;
229 int repc;
230 int rc;
231
232 while ((c = *fmt++) != 0) {
233 repc_specified = B_FALSE;
234 repc = 1;
235
236 if (c == ' ' || c == '\t')
237 continue;
238
239 if (c == '(') {
240 while (((c = *fmt++) != 0) && c != ')')
241 ;
242
243 if (!c)
244 return (SMB_MSGBUF_SUCCESS);
245
246 continue;
247 }
310 }
311 break;
312
313 case 'q': /* get quad */
314 rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
315 if (rc == 0)
316 return (SMB_MSGBUF_UNDERFLOW);
317
318 llvalp = va_arg(ap, uint64_t *);
319 while (repc-- > 0) {
320 *llvalp++ = LE_IN64(mb->scan);
321 mb->scan += sizeof (int64_t);
322 }
323 break;
324
325 case 'u': /* Convert from unicode if flags are set */
326 if (mb->flags & SMB_MSGBUF_UNICODE)
327 goto unicode_translation;
328 /*FALLTHROUGH*/
329
330 case 's': /* get OEM string */
331 cvalpp = va_arg(ap, char **);
332 if (!repc_specified)
333 repc = 0;
334 rc = msgbuf_get_oem_string(mb, cvalpp, repc);
335 if (rc != 0)
336 return (rc);
337 break;
338
339 case 'U': /* get UTF-16 string */
340 unicode_translation:
341 cvalpp = va_arg(ap, char **);
342 if (!repc_specified)
343 repc = 0;
344 rc = msgbuf_get_unicode_string(mb, cvalpp, repc);
345 if (rc != 0)
346 return (rc);
347 break;
348
349 case 'M':
350 if (smb_msgbuf_has_space(mb, 4) == 0)
351 return (SMB_MSGBUF_UNDERFLOW);
352
353 if (mb->scan[0] != 0xFF ||
354 mb->scan[1] != 'S' ||
355 mb->scan[2] != 'M' ||
356 mb->scan[3] != 'B') {
357 return (SMB_MSGBUF_INVALID_HEADER);
358 }
359 mb->scan += 4;
360 break;
361
362 default:
363 return (SMB_MSGBUF_INVALID_FORMAT);
364 }
365 }
366
367 return (SMB_MSGBUF_SUCCESS);
368 }
369
370 /*
371 * msgbuf_get_oem_string
372 *
373 * Decode an OEM string, returning its UTF-8 form in strpp,
374 * allocated using smb_msgbuf_malloc (automatically freed).
375 * If max_bytes != 0, consume at most max_bytes of the mb.
376 * See also: mbc_marshal_get_oem_string
377 */
378 static int
379 msgbuf_get_oem_string(smb_msgbuf_t *mb, char **strpp, int max_bytes)
380 {
381 char *mbs;
382 uint8_t *oembuf = NULL;
383 int oemlen; // len of OEM string, w/o null
384 int datalen; // OtW data len
385 int mbsmax; // max len of ret str
386 int rlen;
387
388 if (max_bytes == 0)
389 max_bytes = 0xffff;
390
391 /*
392 * Determine the OtW data length and OEM string length
393 * Note: oemlen is the string length (w/o null) and
394 * datalen is how much we move mb->scan
395 */
396 datalen = 0;
397 oemlen = 0;
398 for (;;) {
399 if (datalen >= max_bytes)
400 break;
401 /* in-line smb_msgbuf_has_space */
402 if ((mb->scan + datalen) >= mb->end)
403 return (SMB_MSGBUF_UNDERFLOW);
404 datalen++;
405 if (mb->scan[datalen - 1] == 0)
406 break;
407 oemlen++;
408 }
409
410 /*
411 * Get datalen bytes into a temp buffer
412 * sized with room to add a null.
413 * Free oembuf in smb_msgbuf_term
414 */
415 oembuf = smb_msgbuf_malloc(mb, datalen + 1);
416 if (oembuf == NULL)
417 return (SMB_MSGBUF_UNDERFLOW);
418 bcopy(mb->scan, oembuf, datalen);
419 mb->scan += datalen;
420 oembuf[oemlen] = '\0';
421
422 /*
423 * Get the buffer we'll return and convert to UTF-8.
424 * May take as much as double the space.
425 */
426 mbsmax = oemlen * 2;
427 mbs = smb_msgbuf_malloc(mb, mbsmax + 1);
428 if (mbs == NULL)
429 return (SMB_MSGBUF_UNDERFLOW);
430 rlen = smb_oemtombs(mbs, oembuf, mbsmax);
431 if (rlen < 0)
432 return (SMB_MSGBUF_UNDERFLOW);
433 if (rlen > mbsmax)
434 rlen = mbsmax;
435 mbs[rlen] = '\0';
436 *strpp = mbs;
437 return (0);
438 }
439
440 /*
441 * msgbuf_get_unicode_string
442 *
443 * Decode a UTF-16 string, returning its UTF-8 form in strpp,
444 * allocated using smb_msgbuf_malloc (automatically freed).
445 * If max_bytes != 0, consume at most max_bytes of the mb.
446 * See also: mbc_marshal_get_unicode_string
447 */
448 static int
449 msgbuf_get_unicode_string(smb_msgbuf_t *mb, char **strpp, int max_bytes)
450 {
451 char *mbs;
452 uint16_t *wcsbuf = NULL;
453 int wcslen; // wchar count
454 int datalen; // OtW data len
455 size_t mbsmax; // max len of ret str
456 size_t rlen;
457
458 if (max_bytes == 0)
459 max_bytes = 0xffff;
460
461 /*
462 * Unicode strings are always word aligned.
463 */
464 smb_msgbuf_word_align(mb);
465
466 /*
467 * Determine the OtW data length and (WC) string length
468 * Note: wcslen counts 16-bit wide_chars (w/o null),
469 * and datalen is how much we move mb->scan
470 */
471 datalen = 0;
472 wcslen = 0;
473 for (;;) {
474 if (datalen >= max_bytes)
475 break;
476 /* in-line smb_msgbuf_has_space */
477 if ((mb->scan + datalen) >= mb->end)
478 return (SMB_MSGBUF_UNDERFLOW);
479 datalen += 2;
480 if (mb->scan[datalen - 2] == 0 &&
481 mb->scan[datalen - 1] == 0)
482 break;
483 wcslen++;
484 }
485
486 /*
487 * Get datalen bytes into a temp buffer
488 * sized with room to add a (WC) null.
489 * Note: wcsbuf has little-endian order
490 */
491 wcsbuf = smb_msgbuf_malloc(mb, datalen + 2);
492 if (wcsbuf == NULL)
493 return (SMB_MSGBUF_UNDERFLOW);
494 bcopy(mb->scan, wcsbuf, datalen);
495 mb->scan += datalen;
496 wcsbuf[wcslen] = 0;
497
498 /*
499 * Get the buffer we'll return and convert to UTF-8.
500 * May take as much 4X number of wide chars.
501 */
502 mbsmax = wcslen * MTS_MB_CUR_MAX;
503 mbs = smb_msgbuf_malloc(mb, mbsmax + 1);
504 if (mbs == NULL)
505 return (SMB_MSGBUF_UNDERFLOW);
506 rlen = smb_wcstombs(mbs, wcsbuf, mbsmax);
507 if (rlen == (size_t)-1)
508 return (SMB_MSGBUF_UNDERFLOW);
509 if (rlen > mbsmax)
510 rlen = mbsmax;
511 mbs[rlen] = '\0';
512 *strpp = mbs;
513 return (0);
514 }
515
516 /*
517 * smb_msgbuf_encode
518 *
519 * Encode a smb_msgbuf buffer as indicated by the format string using
520 * the variable arg list. This is similar to a sprintf operation.
521 *
522 * On success, returns the number of bytes encoded. Otherwise
523 * returns a -ve error code.
524 */
525 int
526 smb_msgbuf_encode(smb_msgbuf_t *mb, char *fmt, ...)
527 {
528 int rc;
529 uint8_t *orig_scan;
530 va_list ap;
531
532 va_start(ap, fmt);
533 orig_scan = mb->scan;
534 rc = buf_encode(mb, fmt, ap);
535 va_end(ap);
536
545 }
546
547
548 /*
549 * buf_encode
550 *
551 * Private encode function, where the real work of encoding the smb_msgbuf
552 * is done. This function should only be called via smb_msgbuf_encode to
553 * ensure correct behaviour and error handling.
554 */
555 static int
556 buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap)
557 {
558 uint8_t cval;
559 uint16_t wval;
560 uint32_t lval;
561 uint64_t llval;
562 uint8_t *bvalp;
563 char *cvalp;
564 uint8_t c;
565 boolean_t repc_specified;
566 int repc;
567 int rc;
568
569 while ((c = *fmt++) != 0) {
570 repc_specified = B_FALSE;
571 repc = 1;
572
573 if (c == ' ' || c == '\t')
574 continue;
575
576 if (c == '(') {
577 while (((c = *fmt++) != 0) && c != ')')
578 ;
579
580 if (!c)
581 return (SMB_MSGBUF_SUCCESS);
582
583 continue;
584 }
648 }
649 break;
650
651 case 'q': /* put quad */
652 rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
653 if (rc == 0)
654 return (SMB_MSGBUF_OVERFLOW);
655
656 while (repc-- > 0) {
657 llval = va_arg(ap, uint64_t);
658 LE_OUT64(mb->scan, llval);
659 mb->scan += sizeof (uint64_t);
660 }
661 break;
662
663 case 'u': /* conditional unicode */
664 if (mb->flags & SMB_MSGBUF_UNICODE)
665 goto unicode_translation;
666 /* FALLTHROUGH */
667
668 case 's': /* put OEM string */
669 cvalp = va_arg(ap, char *);
670 if (!repc_specified)
671 repc = 0;
672 rc = msgbuf_put_oem_string(mb, cvalp, repc);
673 if (rc != 0)
674 return (rc);
675 break;
676
677 case 'U': /* put UTF-16 string */
678 unicode_translation:
679 cvalp = va_arg(ap, char *);
680 if (!repc_specified)
681 repc = 0;
682 rc = msgbuf_put_unicode_string(mb, cvalp, repc);
683 if (rc != 0)
684 return (rc);
685 break;
686
687 case 'M':
688 if (smb_msgbuf_has_space(mb, 4) == 0)
689 return (SMB_MSGBUF_OVERFLOW);
690
691 *mb->scan++ = 0xFF;
692 *mb->scan++ = 'S';
693 *mb->scan++ = 'M';
694 *mb->scan++ = 'B';
695 break;
696
697 default:
698 return (SMB_MSGBUF_INVALID_FORMAT);
699 }
700 }
701
702 return (SMB_MSGBUF_SUCCESS);
703 }
704
705 /*
706 * Marshal a UTF-8 string (str) into mbc, converting to OEM codeset.
707 * Also write a null unless the repc count limits the length we put.
708 * When (repc > 0) the length we marshal must be exactly repc, and
709 * truncate or pad the mb data as necessary.
710 * See also: mbc_marshal_put_oem_string
711 */
712 static int
713 msgbuf_put_oem_string(smb_msgbuf_t *mb, char *mbs, int repc)
714 {
715 uint8_t *oembuf = NULL;
716 uint8_t *s;
717 int oemlen;
718 int rlen;
719
720 /*
721 * Compute length of converted OEM string,
722 * NOT including null terminator
723 */
724 if ((oemlen = smb_sbequiv_strlen(mbs)) == -1)
725 return (SMB_MSGBUF_DATA_ERROR);
726
727 /*
728 * If repc not specified, put whole string + NULL,
729 * otherwise will truncate or pad as needed.
730 */
731 if (repc <= 0) {
732 repc = oemlen;
733 if ((mb->flags & SMB_MSGBUF_NOTERM) == 0)
734 repc += sizeof (char);
735 }
736 if (smb_msgbuf_has_space(mb, repc) == 0)
737 return (SMB_MSGBUF_OVERFLOW);
738
739 /*
740 * Convert into a temporary buffer
741 * Free oembuf in smb_msgbuf_term.
742 */
743 oembuf = smb_msgbuf_malloc(mb, oemlen + 1);
744 if (oembuf == NULL)
745 return (SMB_MSGBUF_UNDERFLOW);
746 rlen = smb_mbstooem(oembuf, mbs, oemlen);
747 if (rlen < 0)
748 return (SMB_MSGBUF_DATA_ERROR);
749 if (rlen > oemlen)
750 rlen = oemlen;
751 oembuf[rlen] = '\0';
752
753 /*
754 * Copy the converted string into the message,
755 * truncated or paded as required.
756 */
757 s = oembuf;
758 while (repc > 0) {
759 *mb->scan++ = *s;
760 if (*s != '\0')
761 s++;
762 repc--;
763 }
764
765 return (0);
766 }
767
768 /*
769 * Marshal a UTF-8 string (str) into mbc, converting to UTF-16.
770 * Also write a null unless the repc count limits the length.
771 * When (repc > 0) the length we marshal must be exactly repc,
772 * and truncate or pad the mb data as necessary.
773 * See also: mbc_marshal_put_unicode_string
774 */
775 static int
776 msgbuf_put_unicode_string(smb_msgbuf_t *mb, char *mbs, int repc)
777 {
778 smb_wchar_t *wcsbuf = NULL;
779 smb_wchar_t *wp;
780 size_t wcslen, wcsbytes;
781 size_t rlen;
782
783 /* align to word boundary */
784 smb_msgbuf_word_align(mb);
785
786 /*
787 * Compute length of converted UTF-16 string,
788 * NOT including null terminator (in bytes).
789 */
790 wcsbytes = smb_wcequiv_strlen(mbs);
791 if (wcsbytes == (size_t)-1)
792 return (SMB_MSGBUF_DATA_ERROR);
793
794 /*
795 * If repc not specified, put whole string + NULL,
796 * otherwise will truncate or pad as needed.
797 */
798 if (repc <= 0) {
799 repc = (int)wcsbytes;
800 if ((mb->flags & SMB_MSGBUF_NOTERM) == 0)
801 repc += sizeof (smb_wchar_t);
802 }
803 if (smb_msgbuf_has_space(mb, repc) == 0)
804 return (SMB_MSGBUF_OVERFLOW);
805
806 /*
807 * Convert into a temporary buffer
808 * Free wcsbuf in smb_msgbuf_term
809 */
810 wcslen = wcsbytes / 2;
811 wcsbuf = smb_msgbuf_malloc(mb, wcsbytes + 2);
812 if (wcsbuf == NULL)
813 return (SMB_MSGBUF_UNDERFLOW);
814 rlen = smb_mbstowcs(wcsbuf, mbs, wcslen);
815 if (rlen == (size_t)-1)
816 return (SMB_MSGBUF_DATA_ERROR);
817 if (rlen > wcslen)
818 rlen = wcslen;
819 wcsbuf[rlen] = 0;
820
821 /*
822 * Copy the converted string into the message,
823 * truncated or paded as required. Preserve
824 * little-endian order while copying.
825 */
826 wp = wcsbuf;
827 while (repc > 1) {
828 smb_wchar_t wchar = LE_IN16(wp);
829 LE_OUT16(mb->scan, wchar);
830 mb->scan += 2;
831 if (wchar != 0)
832 wp++;
833 repc -= sizeof (smb_wchar_t);
834 }
835 if (repc > 0)
836 *mb->scan++ = '\0';
837
838 return (0);
839 }
840
841 /*
842 * smb_msgbuf_malloc
843 *
844 * Allocate some memory for use with this smb_msgbuf. We increase the
845 * requested size to hold the list pointer and return a pointer
846 * to the area for use by the caller.
847 */
848 static void *
849 smb_msgbuf_malloc(smb_msgbuf_t *mb, size_t size)
850 {
851 smb_msgbuf_mlist_t *item;
852
853 size += sizeof (smb_msgbuf_mlist_t);
854
855 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
856 if ((item = malloc(size)) == NULL)
857 return (NULL);
858 #else
859 item = kmem_alloc(size, KM_SLEEP);
860 #endif
861 item->next = mb->mlist.next;
|