Print this page
NEX-19025 CIFS gets confused with filenames containing enhanced Unicode
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
and: (fix build, check-rtime)
NEX-2460 libfksmbd should not link with libsmb
SMB-56 extended security NTLMSSP, inbound


   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;