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 2019 Nexenta by DDN, 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 737 /* 738 * Convert into a temporary buffer 739 * Free oembuf in smb_msgbuf_term. 740 */ 741 oembuf = smb_msgbuf_malloc(mb, oemlen + 1); 742 if (oembuf == NULL) 743 return (SMB_MSGBUF_UNDERFLOW); 744 rlen = smb_mbstooem(oembuf, mbs, oemlen); 745 if (rlen < 0) 746 return (SMB_MSGBUF_DATA_ERROR); 747 if (rlen > oemlen) 748 rlen = oemlen; 749 oembuf[rlen] = '\0'; 750 751 /* 752 * Copy the converted string into the message, 753 * truncated or paded as required. 754 */ 755 s = oembuf; 756 while (repc > 0) { 757 if (smb_msgbuf_has_space(mb, 1) == 0) 758 return (SMB_MSGBUF_OVERFLOW); 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 smb_wchar_t wchar; 781 size_t wcslen, wcsbytes; 782 size_t rlen; 783 784 /* align to word boundary */ 785 smb_msgbuf_word_align(mb); 786 787 /* 788 * Compute length of converted UTF-16 string, 789 * NOT including null terminator (in bytes). 790 */ 791 wcsbytes = smb_wcequiv_strlen(mbs); 792 if (wcsbytes == (size_t)-1) 793 return (SMB_MSGBUF_DATA_ERROR); 794 795 /* 796 * If repc not specified, put whole string + NULL, 797 * otherwise will truncate or pad as needed. 798 */ 799 if (repc <= 0) { 800 repc = (int)wcsbytes; 801 if ((mb->flags & SMB_MSGBUF_NOTERM) == 0) 802 repc += sizeof (smb_wchar_t); 803 } 804 805 /* 806 * Convert into a temporary buffer 807 * Free wcsbuf in smb_msgbuf_term 808 */ 809 wcslen = wcsbytes / 2; 810 wcsbuf = smb_msgbuf_malloc(mb, wcsbytes + 2); 811 if (wcsbuf == NULL) 812 return (SMB_MSGBUF_UNDERFLOW); 813 rlen = smb_mbstowcs(wcsbuf, mbs, wcslen); 814 if (rlen == (size_t)-1) 815 return (SMB_MSGBUF_DATA_ERROR); 816 if (rlen > wcslen) 817 rlen = wcslen; 818 wcsbuf[rlen] = 0; 819 820 /* 821 * Copy the converted string into the message, 822 * truncated or paded as required. Preserve 823 * little-endian order while copying. 824 */ 825 wp = wcsbuf; 826 while (repc >= sizeof (smb_wchar_t)) { 827 if (smb_msgbuf_has_space(mb, sizeof (smb_wchar_t)) == 0) 828 return (SMB_MSGBUF_OVERFLOW); 829 wchar = LE_IN16(wp); 830 LE_OUT16(mb->scan, wchar); 831 mb->scan += 2; 832 if (wchar != 0) 833 wp++; 834 repc -= sizeof (smb_wchar_t); 835 } 836 if (repc > 0) { 837 if (smb_msgbuf_has_space(mb, 1) == 0) 838 return (SMB_MSGBUF_OVERFLOW); 839 *mb->scan++ = '\0'; 840 } 841 842 return (0); 843 } 844 845 /* 846 * smb_msgbuf_malloc 847 * 848 * Allocate some memory for use with this smb_msgbuf. We increase the 849 * requested size to hold the list pointer and return a pointer 850 * to the area for use by the caller. 851 */ 852 static void * 853 smb_msgbuf_malloc(smb_msgbuf_t *mb, size_t size) 854 { 855 smb_msgbuf_mlist_t *item; 856 857 size += sizeof (smb_msgbuf_mlist_t); 858 859 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) 860 if ((item = malloc(size)) == NULL) 861 return (NULL); 862 #else 863 item = kmem_alloc(size, KM_SLEEP); 864 #endif 865 item->next = mb->mlist.next; 866 item->size = size; 867 mb->mlist.next = item; 868 869 /* 870 * The caller gets a pointer to the address 871 * immediately after the smb_msgbuf_mlist_t. 872 */ 873 return ((void *)(item + 1)); 874 } 875 876 877 /* 878 * smb_msgbuf_chkerc 879 * 880 * Diagnostic function to write an appropriate message to the system log. 881 */ 882 static int 883 smb_msgbuf_chkerc(char *text, int erc) 884 { 885 static struct { 886 int erc; 887 char *name; 888 } etable[] = { 889 { SMB_MSGBUF_SUCCESS, "success" }, 890 { SMB_MSGBUF_UNDERFLOW, "overflow/underflow" }, 891 { SMB_MSGBUF_INVALID_FORMAT, "invalid format" }, 892 { SMB_MSGBUF_INVALID_HEADER, "invalid header" }, 893 { SMB_MSGBUF_DATA_ERROR, "data error" } 894 }; 895 896 int i; 897 898 for (i = 0; i < sizeof (etable)/sizeof (etable[0]); ++i) { 899 if (etable[i].erc == erc) { 900 if (text == 0) 901 text = "smb_msgbuf_chkerc"; 902 break; 903 } 904 } 905 return (erc); 906 } 907