xref: /illumos-gate/usr/src/common/smbsrv/smb_msgbuf.c (revision 142d813a06c6f9a6142e2c276b62129a17a31a65)
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 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 *
82 smb_msgbuf_base(smb_msgbuf_t *mb)
83 {
84 	return (mb->base);
85 }
86 
87 /*
88  * Ensure that the scan is aligned on a word (16-bit) boundary.
89  */
90 void
91 smb_msgbuf_word_align(smb_msgbuf_t *mb)
92 {
93 	mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 1) & ~1);
94 }
95 
96 /*
97  * Ensure that the scan is aligned on a dword (32-bit) boundary.
98  */
99 void
100 smb_msgbuf_dword_align(smb_msgbuf_t *mb)
101 {
102 	mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 3) & ~3);
103 }
104 
105 /*
106  * Checks whether or not the buffer has space for the amount of data
107  * specified. Returns 1 if there is space, otherwise returns 0.
108  */
109 int
110 smb_msgbuf_has_space(smb_msgbuf_t *mb, size_t size)
111 {
112 	if (size > mb->max || (mb->scan + size) > mb->end)
113 		return (0);
114 
115 	return (1);
116 }
117 
118 /*
119  * Set flags the smb_msgbuf.
120  */
121 void
122 smb_msgbuf_fset(smb_msgbuf_t *mb, uint32_t flags)
123 {
124 	mb->flags |= flags;
125 }
126 
127 /*
128  * Clear flags the smb_msgbuf.
129  */
130 void
131 smb_msgbuf_fclear(smb_msgbuf_t *mb, uint32_t flags)
132 {
133 	mb->flags &= ~flags;
134 }
135 
136 /*
137  * smb_msgbuf_init
138  *
139  * Initialize a smb_msgbuf_t structure based on the buffer and size
140  * specified. Both scan and base initially point to the beginning
141  * of the buffer and end points to the limit of the buffer. As
142  * data is added scan should be incremented to point to the next
143  * offset at which data will be written. Max and count are set
144  * to the actual buffer size.
145  */
146 void
147 smb_msgbuf_init(smb_msgbuf_t *mb, uint8_t *buf, size_t size, uint32_t flags)
148 {
149 	mb->scan = mb->base = buf;
150 	mb->max = mb->count = size;
151 	mb->end = &buf[size];
152 	mb->flags = flags;
153 	mb->mlist.next = 0;
154 }
155 
156 
157 /*
158  * smb_msgbuf_term
159  *
160  * Destruct a smb_msgbuf_t. Free any memory hanging off the mlist.
161  */
162 void
163 smb_msgbuf_term(smb_msgbuf_t *mb)
164 {
165 	smb_msgbuf_mlist_t *item = mb->mlist.next;
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 		}
248 
249 		if ('0' <= c && c <= '9') {
250 			repc = 0;
251 			do {
252 				repc = repc * 10 + c - '0';
253 				c = *fmt++;
254 			} while ('0' <= c && c <= '9');
255 			repc_specified = B_TRUE;
256 		} else if (c == '#') {
257 			repc = va_arg(ap, int);
258 			c = *fmt++;
259 			repc_specified = B_TRUE;
260 		}
261 
262 		switch (c) {
263 		case '.':
264 			if (smb_msgbuf_has_space(mb, repc) == 0)
265 				return (SMB_MSGBUF_UNDERFLOW);
266 
267 			mb->scan += repc;
268 			break;
269 
270 		case 'c': /* get char */
271 			if (smb_msgbuf_has_space(mb, repc) == 0)
272 				return (SMB_MSGBUF_UNDERFLOW);
273 
274 			bvalp = va_arg(ap, uint8_t *);
275 			bcopy(mb->scan, bvalp, repc);
276 			mb->scan += repc;
277 			break;
278 
279 		case 'b': /* get byte */
280 			if (smb_msgbuf_has_space(mb, repc) == 0)
281 				return (SMB_MSGBUF_UNDERFLOW);
282 
283 			bvalp = va_arg(ap, uint8_t *);
284 			while (repc-- > 0) {
285 				*bvalp++ = *mb->scan++;
286 			}
287 			break;
288 
289 		case 'w': /* get word */
290 			rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t));
291 			if (rc == 0)
292 				return (SMB_MSGBUF_UNDERFLOW);
293 
294 			wvalp = va_arg(ap, uint16_t *);
295 			while (repc-- > 0) {
296 				*wvalp++ = LE_IN16(mb->scan);
297 				mb->scan += sizeof (uint16_t);
298 			}
299 			break;
300 
301 		case 'l': /* get long */
302 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t));
303 			if (rc == 0)
304 				return (SMB_MSGBUF_UNDERFLOW);
305 
306 			lvalp = va_arg(ap, uint32_t *);
307 			while (repc-- > 0) {
308 				*lvalp++ = LE_IN32(mb->scan);
309 				mb->scan += sizeof (int32_t);
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 
537 	if (rc != SMB_MSGBUF_SUCCESS) {
538 		(void) smb_msgbuf_chkerc("smb_msgbuf_encode", rc);
539 		mb->scan = orig_scan;
540 		return (rc);
541 	}
542 
543 	/*LINTED E_PTRDIFF_OVERFLOW*/
544 	return (mb->scan - orig_scan);
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 		}
585 
586 		if ('0' <= c && c <= '9') {
587 			repc = 0;
588 			do {
589 				repc = repc * 10 + c - '0';
590 				c = *fmt++;
591 			} while ('0' <= c && c <= '9');
592 			repc_specified = B_TRUE;
593 		} else if (c == '#') {
594 			repc = va_arg(ap, int);
595 			c = *fmt++;
596 			repc_specified = B_TRUE;
597 		}
598 
599 		switch (c) {
600 		case '.':
601 			if (smb_msgbuf_has_space(mb, repc) == 0)
602 				return (SMB_MSGBUF_OVERFLOW);
603 
604 			while (repc-- > 0)
605 				*mb->scan++ = 0;
606 			break;
607 
608 		case 'c': /* put char */
609 			if (smb_msgbuf_has_space(mb, repc) == 0)
610 				return (SMB_MSGBUF_OVERFLOW);
611 
612 			bvalp = va_arg(ap, uint8_t *);
613 			bcopy(bvalp, mb->scan, repc);
614 			mb->scan += repc;
615 			break;
616 
617 		case 'b': /* put byte */
618 			if (smb_msgbuf_has_space(mb, repc) == 0)
619 				return (SMB_MSGBUF_OVERFLOW);
620 
621 			while (repc-- > 0) {
622 				cval = va_arg(ap, int);
623 				*mb->scan++ = cval;
624 			}
625 			break;
626 
627 		case 'w': /* put word */
628 			rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t));
629 			if (rc == 0)
630 				return (SMB_MSGBUF_OVERFLOW);
631 
632 			while (repc-- > 0) {
633 				wval = va_arg(ap, int);
634 				LE_OUT16(mb->scan, wval);
635 				mb->scan += sizeof (uint16_t);
636 			}
637 			break;
638 
639 		case 'l': /* put long */
640 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t));
641 			if (rc == 0)
642 				return (SMB_MSGBUF_OVERFLOW);
643 
644 			while (repc-- > 0) {
645 				lval = va_arg(ap, uint32_t);
646 				LE_OUT32(mb->scan, lval);
647 				mb->scan += sizeof (int32_t);
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;
862 	item->size = size;
863 	mb->mlist.next = item;
864 
865 	/*
866 	 * The caller gets a pointer to the address
867 	 * immediately after the smb_msgbuf_mlist_t.
868 	 */
869 	return ((void *)(item + 1));
870 }
871 
872 
873 /*
874  * smb_msgbuf_chkerc
875  *
876  * Diagnostic function to write an appropriate message to the system log.
877  */
878 static int
879 smb_msgbuf_chkerc(char *text, int erc)
880 {
881 	static struct {
882 		int erc;
883 		char *name;
884 	} etable[] = {
885 		{ SMB_MSGBUF_SUCCESS,		"success" },
886 		{ SMB_MSGBUF_UNDERFLOW,		"overflow/underflow" },
887 		{ SMB_MSGBUF_INVALID_FORMAT,	"invalid format" },
888 		{ SMB_MSGBUF_INVALID_HEADER,	"invalid header" },
889 		{ SMB_MSGBUF_DATA_ERROR,	"data error" }
890 	};
891 
892 	int i;
893 
894 	for (i = 0; i < sizeof (etable)/sizeof (etable[0]); ++i) {
895 		if (etable[i].erc == erc) {
896 			if (text == 0)
897 				text = "smb_msgbuf_chkerc";
898 			break;
899 		}
900 	}
901 	return (erc);
902 }
903