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 }