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 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 static void buf_decode_wcs(smb_wchar_t *, smb_wchar_t *, int wcstrlen); 56 57 /* 58 * Returns the offset or number of bytes used within the buffer. 59 */ 60 size_t 61 smb_msgbuf_used(smb_msgbuf_t *mb) 62 { 63 /*LINTED E_PTRDIFF_OVERFLOW*/ 64 return (mb->scan - mb->base); 65 } 66 67 /* 68 * Returns the actual buffer size. 69 */ 70 size_t 71 smb_msgbuf_size(smb_msgbuf_t *mb) 72 { 73 return (mb->max); 74 } 75 76 uint8_t * 77 smb_msgbuf_base(smb_msgbuf_t *mb) 78 { 79 return (mb->base); 80 } 81 82 /* 83 * Ensure that the scan is aligned on a word (16-bit) boundary. 84 */ 85 void 86 smb_msgbuf_word_align(smb_msgbuf_t *mb) 87 { 88 mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 1) & ~1); 89 } 90 91 /* 92 * Ensure that the scan is aligned on a dword (32-bit) boundary. 93 */ 94 void 95 smb_msgbuf_dword_align(smb_msgbuf_t *mb) 96 { 97 mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 3) & ~3); 98 } 99 100 /* 101 * Checks whether or not the buffer has space for the amount of data 102 * specified. Returns 1 if there is space, otherwise returns 0. 103 */ 104 int 105 smb_msgbuf_has_space(smb_msgbuf_t *mb, size_t size) 106 { 107 if (size > mb->max || (mb->scan + size) > mb->end) 108 return (0); 109 110 return (1); 111 } 112 113 /* 114 * Set flags the smb_msgbuf. 115 */ 116 void 117 smb_msgbuf_fset(smb_msgbuf_t *mb, uint32_t flags) 118 { 119 mb->flags |= flags; 120 } 121 122 /* 123 * Clear flags the smb_msgbuf. 124 */ 125 void 126 smb_msgbuf_fclear(smb_msgbuf_t *mb, uint32_t flags) 127 { 128 mb->flags &= ~flags; 129 } 130 131 /* 132 * smb_msgbuf_init 133 * 134 * Initialize a smb_msgbuf_t structure based on the buffer and size 135 * specified. Both scan and base initially point to the beginning 136 * of the buffer and end points to the limit of the buffer. As 137 * data is added scan should be incremented to point to the next 138 * offset at which data will be written. Max and count are set 139 * to the actual buffer size. 140 */ 141 void 142 smb_msgbuf_init(smb_msgbuf_t *mb, uint8_t *buf, size_t size, uint32_t flags) 143 { 144 mb->scan = mb->base = buf; 145 mb->max = mb->count = size; 146 mb->end = &buf[size]; 147 mb->flags = flags; 148 mb->mlist.next = 0; 149 } 150 151 152 /* 153 * smb_msgbuf_term 154 * 155 * Destruct a smb_msgbuf_t. Free any memory hanging off the mlist. 156 */ 157 void 158 smb_msgbuf_term(smb_msgbuf_t *mb) 159 { 160 smb_msgbuf_mlist_t *item = mb->mlist.next; 161 smb_msgbuf_mlist_t *tmp; 162 163 while (item) { 164 tmp = item; 165 item = item->next; 166 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) 167 free(tmp); 168 #else 169 kmem_free(tmp, tmp->size); 170 #endif 171 } 172 } 173 174 175 /* 176 * smb_msgbuf_decode 177 * 178 * Decode a smb_msgbuf buffer as indicated by the format string into 179 * the variable arg list. This is similar to a scanf operation. 180 * 181 * On success, returns the number of bytes encoded. Otherwise 182 * returns a -ve error code. 183 */ 184 int 185 smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...) 186 { 187 int rc; 188 uint8_t *orig_scan; 189 va_list ap; 190 191 va_start(ap, fmt); 192 orig_scan = mb->scan; 193 rc = buf_decode(mb, fmt, ap); 194 va_end(ap); 195 196 if (rc != SMB_MSGBUF_SUCCESS) { 197 (void) smb_msgbuf_chkerc("smb_msgbuf_decode", rc); 198 mb->scan = orig_scan; 199 return (rc); 200 } 201 202 /*LINTED E_PTRDIFF_OVERFLOW*/ 203 return (mb->scan - orig_scan); 204 } 205 206 207 /* 208 * buf_decode 209 * 210 * Private decode function, where the real work of decoding the smb_msgbuf 211 * is done. This function should only be called via smb_msgbuf_decode to 212 * ensure correct behaviour and error handling. 213 */ 214 static int 215 buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap) 216 { 217 uint32_t ival; 218 uint8_t c; 219 uint8_t *cvalp; 220 uint8_t **cvalpp; 221 uint16_t *wvalp; 222 uint32_t *lvalp; 223 uint64_t *llvalp; 224 smb_wchar_t *wcs; 225 int repc; 226 int rc; 227 228 while ((c = *fmt++) != 0) { 229 repc = 1; 230 231 if (c == ' ' || c == '\t') 232 continue; 233 234 if (c == '(') { 235 while (((c = *fmt++) != 0) && c != ')') 236 ; 237 238 if (!c) 239 return (SMB_MSGBUF_SUCCESS); 240 241 continue; 242 } 243 244 if ('0' <= c && c <= '9') { 245 repc = 0; 246 do { 247 repc = repc * 10 + c - '0'; 248 c = *fmt++; 249 } while ('0' <= c && c <= '9'); 250 } else if (c == '#') { 251 repc = va_arg(ap, int); 252 c = *fmt++; 253 } 254 255 switch (c) { 256 case '.': 257 if (smb_msgbuf_has_space(mb, repc) == 0) 258 return (SMB_MSGBUF_UNDERFLOW); 259 260 mb->scan += repc; 261 break; 262 263 case 'c': 264 if (smb_msgbuf_has_space(mb, repc) == 0) 265 return (SMB_MSGBUF_UNDERFLOW); 266 267 cvalp = va_arg(ap, uint8_t *); 268 bcopy(mb->scan, cvalp, repc); 269 mb->scan += repc; 270 break; 271 272 case 'b': 273 if (smb_msgbuf_has_space(mb, repc) == 0) 274 return (SMB_MSGBUF_UNDERFLOW); 275 276 cvalp = va_arg(ap, uint8_t *); 277 while (repc-- > 0) { 278 *cvalp++ = *mb->scan++; 279 } 280 break; 281 282 case 'w': 283 rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t)); 284 if (rc == 0) 285 return (SMB_MSGBUF_UNDERFLOW); 286 287 wvalp = va_arg(ap, uint16_t *); 288 while (repc-- > 0) { 289 *wvalp++ = LE_IN16(mb->scan); 290 mb->scan += sizeof (uint16_t); 291 } 292 break; 293 294 case 'l': 295 rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t)); 296 if (rc == 0) 297 return (SMB_MSGBUF_UNDERFLOW); 298 299 lvalp = va_arg(ap, uint32_t *); 300 while (repc-- > 0) { 301 *lvalp++ = LE_IN32(mb->scan); 302 mb->scan += sizeof (int32_t); 303 } 304 break; 305 306 case 'q': 307 rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t)); 308 if (rc == 0) 309 return (SMB_MSGBUF_UNDERFLOW); 310 311 llvalp = va_arg(ap, uint64_t *); 312 while (repc-- > 0) { 313 *llvalp++ = LE_IN64(mb->scan); 314 mb->scan += sizeof (int64_t); 315 } 316 break; 317 318 case 'u': /* Convert from unicode if flags are set */ 319 if (mb->flags & SMB_MSGBUF_UNICODE) 320 goto unicode_translation; 321 /*FALLTHROUGH*/ 322 323 case 's': 324 ival = strlen((const char *)mb->scan) + 1; 325 if (smb_msgbuf_has_space(mb, ival) == 0) 326 return (SMB_MSGBUF_UNDERFLOW); 327 328 if ((cvalp = smb_msgbuf_malloc(mb, ival * 2)) == 0) 329 return (SMB_MSGBUF_UNDERFLOW); 330 331 if ((ival = smb_stombs((char *)cvalp, 332 (char *)mb->scan, ival * 2)) == 333 (uint32_t)-1) { 334 return (SMB_MSGBUF_DATA_ERROR); 335 } 336 337 cvalpp = va_arg(ap, uint8_t **); 338 *cvalpp = cvalp; 339 mb->scan += (ival+1); 340 break; 341 342 case 'U': /* Convert from unicode */ 343 unicode_translation: 344 /* 345 * Unicode strings are always word aligned. 346 * The malloc'd area is larger than the 347 * original string because the UTF-8 chars 348 * may be longer than the wide-chars. 349 */ 350 smb_msgbuf_word_align(mb); 351 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 352 wcs = (smb_wchar_t *)mb->scan; 353 354 /* count the null wchar */ 355 repc = sizeof (smb_wchar_t); 356 while (*wcs++) 357 repc += sizeof (smb_wchar_t); 358 359 if (smb_msgbuf_has_space(mb, repc) == 0) 360 return (SMB_MSGBUF_UNDERFLOW); 361 362 /* Decode wchar string into host byte-order */ 363 if ((wcs = smb_msgbuf_malloc(mb, repc)) == 0) 364 return (SMB_MSGBUF_UNDERFLOW); 365 366 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 367 buf_decode_wcs(wcs, (smb_wchar_t *)mb->scan, 368 repc / sizeof (smb_wchar_t)); 369 370 /* Get space for translated string */ 371 if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0) 372 return (SMB_MSGBUF_UNDERFLOW); 373 374 /* Translate string */ 375 (void) smb_wcstombs((char *)cvalp, wcs, repc * 2); 376 377 cvalpp = va_arg(ap, uint8_t **); 378 *cvalpp = cvalp; 379 mb->scan += repc; 380 break; 381 382 case 'M': 383 if (smb_msgbuf_has_space(mb, 4) == 0) 384 return (SMB_MSGBUF_UNDERFLOW); 385 386 if (mb->scan[0] != 0xFF || 387 mb->scan[1] != 'S' || 388 mb->scan[2] != 'M' || 389 mb->scan[3] != 'B') { 390 return (SMB_MSGBUF_INVALID_HEADER); 391 } 392 mb->scan += 4; 393 break; 394 395 default: 396 return (SMB_MSGBUF_INVALID_FORMAT); 397 } 398 } 399 400 return (SMB_MSGBUF_SUCCESS); 401 } 402 403 404 /* 405 * smb_msgbuf_encode 406 * 407 * Encode a smb_msgbuf buffer as indicated by the format string using 408 * the variable arg list. This is similar to a sprintf operation. 409 * 410 * On success, returns the number of bytes encoded. Otherwise 411 * returns a -ve error code. 412 */ 413 int 414 smb_msgbuf_encode(smb_msgbuf_t *mb, char *fmt, ...) 415 { 416 int rc; 417 uint8_t *orig_scan; 418 va_list ap; 419 420 va_start(ap, fmt); 421 orig_scan = mb->scan; 422 rc = buf_encode(mb, fmt, ap); 423 va_end(ap); 424 425 if (rc != SMB_MSGBUF_SUCCESS) { 426 (void) smb_msgbuf_chkerc("smb_msgbuf_encode", rc); 427 mb->scan = orig_scan; 428 return (rc); 429 } 430 431 /*LINTED E_PTRDIFF_OVERFLOW*/ 432 return (mb->scan - orig_scan); 433 } 434 435 436 /* 437 * buf_encode 438 * 439 * Private encode function, where the real work of encoding the smb_msgbuf 440 * is done. This function should only be called via smb_msgbuf_encode to 441 * ensure correct behaviour and error handling. 442 */ 443 static int 444 buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap) 445 { 446 uint8_t cval; 447 uint16_t wval; 448 uint32_t lval; 449 uint64_t llval; 450 uint32_t ival; 451 uint8_t *cvalp; 452 uint8_t c; 453 smb_wchar_t wcval; 454 int count; 455 int repc = 1; 456 int rc; 457 458 while ((c = *fmt++) != 0) { 459 repc = 1; 460 461 if (c == ' ' || c == '\t') 462 continue; 463 464 if (c == '(') { 465 while (((c = *fmt++) != 0) && c != ')') 466 ; 467 468 if (!c) 469 return (SMB_MSGBUF_SUCCESS); 470 471 continue; 472 } 473 474 if ('0' <= c && c <= '9') { 475 repc = 0; 476 do { 477 repc = repc * 10 + c - '0'; 478 c = *fmt++; 479 } while ('0' <= c && c <= '9'); 480 } else if (c == '#') { 481 repc = va_arg(ap, int); 482 c = *fmt++; 483 } 484 485 switch (c) { 486 case '.': 487 if (smb_msgbuf_has_space(mb, repc) == 0) 488 return (SMB_MSGBUF_OVERFLOW); 489 490 while (repc-- > 0) 491 *mb->scan++ = 0; 492 break; 493 494 case 'c': 495 if (smb_msgbuf_has_space(mb, repc) == 0) 496 return (SMB_MSGBUF_OVERFLOW); 497 498 cvalp = va_arg(ap, uint8_t *); 499 bcopy(cvalp, mb->scan, repc); 500 mb->scan += repc; 501 break; 502 503 case 'b': 504 if (smb_msgbuf_has_space(mb, repc) == 0) 505 return (SMB_MSGBUF_OVERFLOW); 506 507 while (repc-- > 0) { 508 cval = va_arg(ap, int); 509 *mb->scan++ = cval; 510 } 511 break; 512 513 case 'w': 514 rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t)); 515 if (rc == 0) 516 return (SMB_MSGBUF_OVERFLOW); 517 518 while (repc-- > 0) { 519 wval = va_arg(ap, int); 520 LE_OUT16(mb->scan, wval); 521 mb->scan += sizeof (uint16_t); 522 } 523 break; 524 525 case 'l': 526 rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t)); 527 if (rc == 0) 528 return (SMB_MSGBUF_OVERFLOW); 529 530 while (repc-- > 0) { 531 lval = va_arg(ap, uint32_t); 532 LE_OUT32(mb->scan, lval); 533 mb->scan += sizeof (int32_t); 534 } 535 break; 536 537 case 'q': 538 rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t)); 539 if (rc == 0) 540 return (SMB_MSGBUF_OVERFLOW); 541 542 while (repc-- > 0) { 543 llval = va_arg(ap, uint64_t); 544 LE_OUT64(mb->scan, llval); 545 mb->scan += sizeof (uint64_t); 546 } 547 break; 548 549 case 'u': /* conditional unicode */ 550 if (mb->flags & SMB_MSGBUF_UNICODE) 551 goto unicode_translation; 552 /* FALLTHROUGH */ 553 554 case 's': 555 cvalp = va_arg(ap, uint8_t *); 556 ival = strlen((const char *)cvalp) + 1; 557 558 if (smb_msgbuf_has_space(mb, ival) == 0) 559 return (SMB_MSGBUF_OVERFLOW); 560 561 ival = 562 smb_mbstos((char *)mb->scan, (const char *)cvalp); 563 mb->scan += ival + 1; 564 break; 565 566 case 'U': /* unicode */ 567 unicode_translation: 568 /* 569 * Unicode strings are always word aligned. 570 */ 571 smb_msgbuf_word_align(mb); 572 cvalp = va_arg(ap, uint8_t *); 573 574 for (;;) { 575 rc = smb_msgbuf_has_space(mb, 576 sizeof (smb_wchar_t)); 577 if (rc == 0) 578 return (SMB_MSGBUF_OVERFLOW); 579 580 count = smb_mbtowc(&wcval, (const char *)cvalp, 581 MTS_MB_CHAR_MAX); 582 583 if (count < 0) { 584 return (SMB_MSGBUF_DATA_ERROR); 585 } else if (count == 0) { 586 /* 587 * No longer need to do this now that 588 * mbtowc correctly writes the null 589 * before returning zero but paranoia 590 * wins. 591 */ 592 wcval = 0; 593 count = 1; 594 } 595 596 /* Write wchar in wire-format */ 597 LE_OUT16(mb->scan, wcval); 598 599 if (*cvalp == 0) { 600 /* 601 * End of string. Check to see whether 602 * or not to include the null 603 * terminator. 604 */ 605 if ((mb->flags & SMB_MSGBUF_NOTERM) == 606 0) 607 mb->scan += 608 sizeof (smb_wchar_t); 609 break; 610 } 611 612 mb->scan += sizeof (smb_wchar_t); 613 cvalp += count; 614 } 615 break; 616 617 case 'M': 618 if (smb_msgbuf_has_space(mb, 4) == 0) 619 return (SMB_MSGBUF_OVERFLOW); 620 621 *mb->scan++ = 0xFF; 622 *mb->scan++ = 'S'; 623 *mb->scan++ = 'M'; 624 *mb->scan++ = 'B'; 625 break; 626 627 default: 628 return (SMB_MSGBUF_INVALID_FORMAT); 629 } 630 } 631 632 return (SMB_MSGBUF_SUCCESS); 633 } 634 635 636 /* 637 * smb_msgbuf_malloc 638 * 639 * Allocate some memory for use with this smb_msgbuf. We increase the 640 * requested size to hold the list pointer and return a pointer 641 * to the area for use by the caller. 642 */ 643 static void * 644 smb_msgbuf_malloc(smb_msgbuf_t *mb, size_t size) 645 { 646 smb_msgbuf_mlist_t *item; 647 648 size += sizeof (smb_msgbuf_mlist_t); 649 650 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) 651 if ((item = malloc(size)) == NULL) 652 return (NULL); 653 #else 654 item = kmem_alloc(size, KM_SLEEP); 655 #endif 656 item->next = mb->mlist.next; 657 item->size = size; 658 mb->mlist.next = item; 659 660 /* 661 * The caller gets a pointer to the address 662 * immediately after the smb_msgbuf_mlist_t. 663 */ 664 return ((void *)(item + 1)); 665 } 666 667 668 /* 669 * smb_msgbuf_chkerc 670 * 671 * Diagnostic function to write an appropriate message to the system log. 672 */ 673 static int 674 smb_msgbuf_chkerc(char *text, int erc) 675 { 676 static struct { 677 int erc; 678 char *name; 679 } etable[] = { 680 { SMB_MSGBUF_SUCCESS, "success" }, 681 { SMB_MSGBUF_UNDERFLOW, "overflow/underflow" }, 682 { SMB_MSGBUF_INVALID_FORMAT, "invalid format" }, 683 { SMB_MSGBUF_INVALID_HEADER, "invalid header" }, 684 { SMB_MSGBUF_DATA_ERROR, "data error" } 685 }; 686 687 int i; 688 689 for (i = 0; i < sizeof (etable)/sizeof (etable[0]); ++i) { 690 if (etable[i].erc == erc) { 691 if (text == 0) 692 text = "smb_msgbuf_chkerc"; 693 break; 694 } 695 } 696 return (erc); 697 } 698 699 static void 700 buf_decode_wcs(smb_wchar_t *dst_wcstr, smb_wchar_t *src_wcstr, int wcstrlen) 701 { 702 int i; 703 704 for (i = 0; i < wcstrlen; i++) { 705 *dst_wcstr = LE_IN16(src_wcstr); 706 dst_wcstr++; 707 src_wcstr++; 708 } 709 } 710