1/* 2 * Copyright (c) 2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <libkern/OSAtomic.h> 25#include <sys/smb_apple.h> 26 27#include <netsmb/smb.h> 28#include <netsmb/smb_2.h> 29#include <netsmb/smb_conn.h> 30#include <netsmb/smb_rq.h> 31#include <netsmb/smb_rq_2.h> 32#include <netsmb/smb_packets_2.h> 33#include <smbclient/ntstatus.h> 34 35static int smb2_rq_init_internal(struct smb_rq *rqp, struct smb_connobj *obj, 36 u_char cmd, uint32_t *rq_len, int rq_flags, 37 vfs_context_t context); 38static int smb2_rq_new(struct smb_rq *rqp); 39 40 41/* 42 * Adds padding to 8 byte boundary for SMB2 compound requests 43 */ 44void 45smb2_rq_align8(struct smb_rq *rqp) 46{ 47 size_t pad_bytes = 0; 48 struct mbchain *mbp; 49 50 if ((rqp->sr_rq.mb_len % 8) != 0) { 51 /* Next request MUST start on next 8 byte boundary! */ 52 pad_bytes = 8 - (rqp->sr_rq.mb_len % 8); 53 smb_rq_getrequest(rqp, &mbp); 54 mb_put_mem(mbp, NULL, pad_bytes, MB_MZERO); 55 } 56} 57 58/* 59 * The object is either a share or a vc in either case the calling routine 60 * must have a reference on the object before calling this routine. 61 * 62 * Be careful to use the correct maco SSTOCP or VCTOCP to get the correct 63 * smb_connobj to pass in which depends on the type of packet you are 64 * sending. If you pass in the wrong obj, odd behavior will result. 65 */ 66int 67smb2_rq_alloc(struct smb_connobj *obj, u_char cmd, uint32_t *rq_len, 68 vfs_context_t context, struct smb_rq **rqpp) 69{ 70 struct smb_rq *rqp; 71 int error; 72 73 /* 74 * This function allocates smb_rq and creates the SMB2 header 75 */ 76 MALLOC(rqp, struct smb_rq *, sizeof(*rqp), M_SMBRQ, M_WAITOK); 77 if (rqp == NULL) 78 return ENOMEM; 79 80 error = smb2_rq_init_internal(rqp, obj, cmd, rq_len, SMBR_ALLOCED, context); 81 if (!error) { 82 /* On error, smb2_rq_init_internal will free the rqp */ 83 *rqpp = rqp; 84 } 85 86 return error; 87} 88 89/* 90 * Similar to smb_rq_bend except matches with smb2_rq_bstart32 meaning it 91 * assumes the use of rqp->sr_lcount 92 */ 93void 94smb_rq_bend32(struct smb_rq *rqp) 95{ 96 uint32_t bcnt; 97 98 DBG_ASSERT(rqp->sr_lcount); 99 if (rqp->sr_lcount == NULL) { 100 SMBERROR("no lcount\n"); /* actually panic */ 101 return; 102 } 103 /* 104 * Byte Count field should be ignored when dealing with SMB_CAP_LARGE_WRITEX 105 * or SMB_CAP_LARGE_READX messages. So we set it to zero in these cases. 106 */ 107 if (((VC_CAPS(rqp->sr_vc) & SMB_CAP_LARGE_READX) && (rqp->sr_cmd == SMB_COM_READ_ANDX)) || 108 ((VC_CAPS(rqp->sr_vc) & SMB_CAP_LARGE_WRITEX) && (rqp->sr_cmd == SMB_COM_WRITE_ANDX))) { 109 /* SAMBA 4 doesn't like the byte count to be zero */ 110 if (rqp->sr_rq.mb_count > 0xffffffff) 111 bcnt = 0; /* Set the byte count to zero here */ 112 else 113 bcnt = (uint32_t)rqp->sr_rq.mb_count; 114 } else if (rqp->sr_rq.mb_count > 0xffffffff) { 115 SMBERROR("byte count too large (%ld)\n", rqp->sr_rq.mb_count); 116 bcnt = 0xfffffff; /* not sure what else to do here */ 117 } else 118 bcnt = (uint32_t)rqp->sr_rq.mb_count; 119 120 *rqp->sr_lcount = htolel(bcnt); 121} 122 123/* 124 * Similar to smb_rq_bstart except uses any uint16 len field 125 */ 126void 127smb2_rq_bstart(struct smb_rq *rqp, uint16_t *len_ptr) 128{ 129 rqp->sr_bcount = len_ptr; 130 rqp->sr_rq.mb_count = 0; 131} 132 133/* 134 * Similar to smb_rq_bstart except uses any uint32 len field 135 */ 136void 137smb2_rq_bstart32(struct smb_rq *rqp, uint32_t *len_ptr) 138{ 139 rqp->sr_lcount = len_ptr; 140 rqp->sr_rq.mb_count = 0; 141} 142 143/* 144 * SMB 2.x crediting 145 * 146 * CreditCharge - number of credits consumed by response 147 * CreditRequest/CreditResponse - used to request more credits. 148 * >1 = requesting more credits 149 * 1 = maintain current number of credits 150 * 0 = decrease number of current credits 151 * 152 * Calls that can use multi credits are 153 * Read/Write - len is the IO size 154 * IOCTL - dont think we support any of these specific ones 155 * Query Dir - output buffer length 156 * Change Notify - output buffer length 157 * Query Info - output buffer length 158 * Set Info - output buffer length 159 * 160 * CreditCharge = ((RequestLength - 1) / (64 * 1024)) + 1; 161 * Dont forget to increment the MessageIDs by the same amount 162 */ 163uint32_t 164smb2_rq_credit_check(struct smb_rq *rqp, uint32_t len) 165{ 166 /* This function returns the allowable length based on avail credits */ 167 int16_t credit_charge16; 168 int32_t credit_charge32; 169 int32_t curr_credits; 170 uint32_t ret_len = len; 171 172 /* 173 * VC Credit lock must be held before calling this function 174 */ 175 176 /* Do simple checks first */ 177 if (len <= (64 * 1024)) { 178 /* default of one credit is fine and length is fine as is */ 179 return ret_len; 180 } 181 182 /* How many credits do we have? */ 183 curr_credits = OSAddAtomic(0, &rqp->sr_vc->vc_credits_granted); 184 185 if (curr_credits <= kCREDIT_LOW_WATER) { 186 /* 187 * Running low on credits, so stay with just default of one credit. 188 * Adjust len to be just 64K to fit in one credit 189 */ 190 ret_len = 64 * 1024; 191 return ret_len; 192 } 193 194 /* how many credits are needed? */ 195 credit_charge16 = ((len - 1) / (64 * 1024)) + 1; 196 credit_charge32 = credit_charge16; 197 198 if ((credit_charge32 + kCREDIT_LOW_WATER) > curr_credits) { 199 /* Not enough credits left, so reduce len of request */ 200 ret_len = (curr_credits - kCREDIT_LOW_WATER) * (64 * 1024); 201 credit_charge16 = curr_credits - kCREDIT_LOW_WATER; 202 } 203 204 /* Update credit charge */ 205 rqp->sr_creditcharge = credit_charge16; 206 207 DBG_ASSERT(ret_len != 0); 208 return ret_len; 209} 210 211/* 212 * Decrement vc_credits_granted by number of credits charged in request 213 * See smb2_rq_credit_check() for longer description 214 */ 215static int 216smb2_rq_credit_decrement(struct smb_rq *rqp, uint32_t *rq_len) 217{ 218 int32_t curr_credits; 219 int16_t credit_charge; 220 struct timespec ts; 221 int ret; 222 int error = 0; 223 struct smb_vc *vcp; 224 uint32_t sleep_cnt, i; 225 uint64_t message_id_diff = 0; 226 227 if (rqp == NULL) { 228 SMBERROR("rqp is NULL\n"); 229 return ENOMEM; 230 } 231 232 if (rqp->sr_vc == NULL) { 233 SMBERROR("rqp->sr_vc is NULL\n"); 234 return ENOMEM; 235 } 236 vcp = rqp->sr_vc; 237 238 SMBC_CREDIT_LOCK(vcp); 239 240 switch (rqp->sr_command) { 241 case SMB2_NEGOTIATE: 242 /* 243 * Negotiate is a special case where credit charge and credits 244 * requested are both 0. Its expected server will grant at least 1 245 * credit in Neg rsp 246 */ 247 rqp->sr_creditcharge = 0; 248 rqp->sr_creditsrequested = 0; 249 250 /* Reset starting credits to be 0 in case of reconnect */ 251 vcp->vc_credits_granted = 0; 252 vcp->vc_credits_ss_granted = 0; 253 vcp->vc_credits_max = 0; 254 255 goto out; 256 257 case SMB2_SESSION_SETUP: 258 /* 259 * We need more than a minimum of 3 credits in order to send 260 * at least one compound request instead of hanging waiting for 261 * more credits. 262 */ 263 rqp->sr_creditsrequested = kCREDIT_REQUEST_AMT; 264 goto out; 265 266 case SMB2_TREE_CONNECT: 267 case SMB2_LOGOFF: 268 /* 269 * These commands are used during reconnect and we assume that 270 * we always have one credit to use. 271 */ 272 goto out; 273 274 default: 275 if (!(vcp->vc_flags & SMBV_SMB2)) { 276 /* 277 * Huh? Why are we trying to send SMB 2.x request on SMB 1.x 278 * connection. This is not allowed. Need to find the code path 279 * that got to here and fix it. 280 */ 281 SMBERROR("SMB 2.x not allowed on SMB 1.x connection. cmd = %x\n", 282 rqp->sr_command); 283 error = ERPCMISMATCH; 284 goto out; 285 } 286 287 if (rqp->sr_context == vcp->vc_iod->iod_context) { 288 /* 289 * More reconnect commands, assume that we always have one 290 * credit to use 291 */ 292 goto out; 293 } 294 295 /* All other requests must go through regular crediting code */ 296 break; 297 } 298 299 /* 300 * Check to see if need to pause sending until we get more credits 301 * Two ways to run out of credits. 302 * 1) Just have no credits left 303 * 2) (curr message ID) - (oldest pending message ID) > current credits 304 */ 305 for (;;) { 306 curr_credits = OSAddAtomic(0, &vcp->vc_credits_granted); 307 if (curr_credits >= kCREDIT_MIN_AMT) { 308 if (vcp->vc_req_pending == 0) { 309 /* Have enough credits and no pending reqs, so go send it */ 310 break; 311 } 312 313 /* Have a pending request, see if send window is open */ 314 if (vcp->vc_message_id > vcp->vc_oldest_message_id) { 315 message_id_diff = vcp->vc_message_id - vcp->vc_oldest_message_id; 316 } 317 else { 318 /* Must have wrapped around */ 319 message_id_diff = UINT64_MAX - vcp->vc_oldest_message_id; 320 message_id_diff += vcp->vc_message_id; 321 } 322 323 if (message_id_diff <= (uint64_t) curr_credits) { 324 /* Send window still open, so go send it */ 325 break; 326 } 327 } 328 329 if (rqp->sr_command == SMB2_ECHO) { 330 /* 331 * Can not block waiting here for credits for an Echo request. 332 * Echo request is sent by smb_iod_sendall() and that is the same 333 * function that times out requests that have not gotten their 334 * replies. Just skip sending the Echo request and return an error. 335 */ 336 SMBC_CREDIT_UNLOCK(vcp); 337 return ENOBUFS; 338 } 339 340 /* If the share is going away, just return immediately */ 341 if ((rqp->sr_share) && 342 (rqp->sr_share->ss_going_away) && 343 (rqp->sr_share->ss_going_away(rqp->sr_share))) { 344 SMBC_CREDIT_UNLOCK(vcp); 345 return ENXIO; 346 } 347 348 /* Block until we get more credits */ 349 SMBDEBUG("Wait for credits curr %d max %d curr ID %lld pending ID %lld vc_credits_wait %d\n", 350 curr_credits, vcp->vc_credits_max, 351 vcp->vc_message_id, vcp->vc_oldest_message_id, 352 vcp->vc_credits_wait); 353 354 /* Only wait a max of 60 seconds waiting for credits */ 355 sleep_cnt = 60; 356 ts.tv_sec = 1; 357 ts.tv_nsec = 0; 358 359 for (i = 1; i <= sleep_cnt; i++) { 360 /* Indicate that we are sleeping by incrementing wait counter */ 361 OSAddAtomic(1, &vcp->vc_credits_wait); 362 363 ret = msleep(&vcp->vc_credits_wait, SMBC_CREDIT_LOCKPTR(vcp), 364 PWAIT, "vc-credits-wait", &ts); 365 366 if (ret == EWOULDBLOCK) { 367 /* Timed out, so decrement wait counter */ 368 OSAddAtomic(-1, &vcp->vc_credits_wait); 369 370 /* If the share is going away, just return immediately */ 371 if ((rqp->sr_share) && 372 (rqp->sr_share->ss_going_away) && 373 (rqp->sr_share->ss_going_away(rqp->sr_share))) { 374 SMBC_CREDIT_UNLOCK(vcp); 375 return ENXIO; 376 } 377 378 /* Loop again and wait some more */ 379 } 380 else { 381 /* 382 * If we are woken up and its not EWOULDBLOCK, then must 383 * have credits now, go and check. 384 */ 385 break; 386 } 387 } 388 389 if (ret == EWOULDBLOCK) { 390 /* 391 * Something really went wrong here. We should not have to wait 392 * this long to get any credits. Force a reconnect. 393 */ 394 SMBERROR("Timed out waiting for credits %d \n", ret); 395 396 /* Reconnect requests will need the credit lock so free it */ 397 SMBC_CREDIT_UNLOCK(vcp); 398 399 (void) smb_vc_force_reconnect(vcp); 400 401 /* Reconnect is done now, reacquire credit lock and try again */ 402 SMBC_CREDIT_LOCK(vcp); 403 } 404 } 405 406 if (rq_len != NULL) { 407 /* Possible multi credit request */ 408 *rq_len = smb2_rq_credit_check(rqp, *rq_len); 409 } 410 411 credit_charge = rqp->sr_creditcharge; 412 413 /* Decrement number of credits we have left */ 414 OSAddAtomic(-(credit_charge), &vcp->vc_credits_granted); 415 416 /* Message IDs are set right before the request is sent */ 417 418 /* 419 * Check to see if need to request more credits. 420 */ 421 curr_credits = OSAddAtomic(0, &vcp->vc_credits_granted); 422 423 if (curr_credits < kCREDIT_MAX_AMT) { 424 /* 425 * Currently the client keeps trying to get more and more credits 426 * until get kCREDIT_MAX_AMT. Essentially its up to the server to 427 * throttle back our client. 428 * Optionally, we could change kCREDIT_MAX_AMT to be kCREDIT_LOW_WATER 429 * so that we only request more credits when we need them. 430 */ 431 rqp->sr_creditsrequested = kCREDIT_REQUEST_AMT; 432 433 /* Dont access sr_creditreqp as its not set up yet */ 434 } 435 436 if (curr_credits < 0) { 437 SMBERROR("credit count %d < 0 \n", curr_credits); 438 } 439 440out: 441 SMBC_CREDIT_UNLOCK(vcp); 442 443 return error; 444} 445 446/* 447 * Increment vc_credits_granted by number of credits granted by server 448 * See smb2_rq_credit_check() for longer description 449 */ 450int 451smb2_rq_credit_increment(struct smb_rq *rqp) 452{ 453 int32_t curr_credits; 454 struct smb_vc *vcp; 455 456 if (rqp == NULL) { 457 SMBERROR("rqp is NULL\n"); 458 return (ENOMEM); 459 } 460 461 if (rqp->sr_vc == NULL) { 462 SMBERROR("rqp->sr_vc is NULL in %lld \n", rqp->sr_messageid); 463 return (ENOMEM); 464 } 465 vcp = rqp->sr_vc; 466 467 if (rqp->sr_rspcreditsgranted == 0) { 468 /* Nothing to do */ 469 return (0); 470 } 471 472 switch (rqp->sr_command) { 473 case SMB2_NEGOTIATE: 474 case SMB2_TREE_CONNECT: 475 case SMB2_LOGOFF: 476 /* 477 * These commands are used during reconnect and we assume 478 * they are always granted one credit back. 479 */ 480 return (0); 481 482 case SMB2_SESSION_SETUP: 483 /* 484 * Save credits granted from Session Setup in separate variable. 485 * Once we are ready to start tracking credits then we will set 486 * this number as our starting number of credits. 487 */ 488 SMBC_CREDIT_LOCK(vcp); 489 OSAddAtomic(rqp->sr_rspcreditsgranted, &vcp->vc_credits_ss_granted); 490 SMBC_CREDIT_UNLOCK(vcp); 491 return (0); 492 493 default: 494 if (rqp->sr_context == vcp->vc_iod->iod_context) { 495 /* 496 * More reconnect commands, assume that we always are granted 497 * one credit back 498 */ 499 return (0); 500 } 501 502 /* All other responses must go through regular crediting code */ 503 break; 504 } 505 506 SMBC_CREDIT_LOCK(vcp); 507 508 OSAddAtomic(rqp->sr_rspcreditsgranted, &vcp->vc_credits_granted); 509 510 /* Keep track of max number of credits that server has granted us */ 511 curr_credits = OSAddAtomic(0, &vcp->vc_credits_granted); 512 if (vcp->vc_credits_max < (uint32_t) curr_credits) { 513 vcp->vc_credits_max = curr_credits; 514 515 /* Set an upper limit to the number of credits that client can use */ 516 if (vcp->vc_credits_max > kCREDIT_MAX_AMT) { 517 vcp->vc_credits_max = kCREDIT_MAX_AMT; 518 } 519 } 520 521 /* Wake up any requests waiting for more credits */ 522 if (vcp->vc_credits_wait) { 523 OSAddAtomic(-1, &vcp->vc_credits_wait); 524 wakeup(&vcp->vc_credits_wait); 525 } 526 527 SMBC_CREDIT_UNLOCK(vcp); 528 529 return 0; 530} 531 532/* 533 * Set starting number of credits. 534 * For the Login/Reconnect commands of Negotiate, Session Setup, Tree Connect, 535 * and Log Off, we assume we always have 1 credit and that we always get 1 536 * credit back. The credit count is not incremented during this time. 537 * During a Login, this is fine as its a synchronous operation 538 * with just one request being done at a time. During Reconnect, this is 539 * important as you may have several threads waiting for credits when we go 540 * into reconnect. In Reconnect, the credit count goes back to 0. We dont want 541 * to increment our credit count on the replies to these commands as then the 542 * other threads may grab all the credits and then the reconnect commands would 543 * be stuck waiting for more credits that will not arrive. Once Reconnect is 544 * done, then we set the credit count to be the number of credits that we got 545 * back from the Session Setup replies. 546 * 547 * Each Session Setup request will ask for more credits. The credits granted 548 * in those replies are saved in a separate counter. When crediting is allowed 549 * to start up, smb2_rq_credit_start() is called. 550 * smb2_rq_credit_start() is called in two cases: 551 * 1) Not in reconnect, this is called after Session Setup is done 552 * 2) In reconnect, called after reconnect finishes 553 * 554 * If credits is 0, then set credits to the ones granted by the Session Setup 555 * responses. If credits is non zero, then set it to that value (failed 556 * reconnect situation). 557 * 558 * The very next commands sent after Logging in or after Reconnect may be 559 * compound request, we need to start with at least > 3 credits. 560 */ 561void 562smb2_rq_credit_start(struct smb_vc *vcp, uint16_t credits) 563{ 564 DBG_ASSERT(vcp != NULL); 565 566 SMBC_CREDIT_LOCK(vcp); 567 568 if (credits == 0) { 569 /* 570 * Set our starting number of credits to the number granted to us from 571 * the Session Setup Responses. 572 */ 573 OSAddAtomic(vcp->vc_credits_ss_granted, &vcp->vc_credits_granted); 574 } 575 else { 576 /* Set to passed in value. Should be due to failed reconnect */ 577 OSAddAtomic(credits, &vcp->vc_credits_granted); 578 } 579 580 /* Set max credits to same value */ 581 vcp->vc_credits_max = vcp->vc_credits_granted; 582 583 /* Clear out oldest message ID to open send window */ 584 vcp->vc_req_pending = 0; 585 vcp->vc_oldest_message_id = 0; 586 587 /* Wake up any requests waiting for more credits */ 588 if (vcp->vc_credits_wait) { 589 OSAddAtomic(-1, &vcp->vc_credits_wait); 590 wakeup(&vcp->vc_credits_wait); 591 } 592 593 SMBC_CREDIT_UNLOCK(vcp); 594} 595 596/* 597 * The object is either a share or a vc in either case the calling routine 598 * must have a reference on the object before calling this routine. 599 */ 600static int 601smb2_rq_init_internal(struct smb_rq *rqp, struct smb_connobj *obj, u_char cmd, 602 uint32_t *rq_len, int rq_flags, vfs_context_t context) 603{ 604 int error; 605 606 /* Fill in the smb_rq */ 607 bzero(rqp, sizeof(*rqp)); 608 rqp->sr_flags |= rq_flags; 609 lck_mtx_init(&rqp->sr_slock, srs_lck_group, srs_lck_attr); 610 error = smb_rq_getenv(obj, &rqp->sr_vc, &rqp->sr_share); 611 if (error) 612 goto done; 613 614 error = smb_vc_access(rqp->sr_vc, context); 615 if (error) 616 goto done; 617 618 rqp->sr_command = cmd; 619 rqp->sr_creditcharge = 1; 620 rqp->sr_creditsrequested = 1; 621 rqp->sr_rqtreeid = 0; 622 rqp->sr_rqsessionid = rqp->sr_vc->vc_session_id; 623 624 rqp->sr_context = context; 625 rqp->sr_extflags |= SMB2_REQUEST; 626 627 /* 628 * Decrement current credit count 629 * ASSUMPTION - a built request will always get sent, otherwise the credit 630 * counts and message ids will get out of sync. 631 */ 632 error = smb2_rq_credit_decrement(rqp, rq_len); 633 if (error) { 634 /* 635 * if got an error, then must not have used any credits and we did not 636 * send the request. Set sr_creditcharge to 0 so we do not try to 637 * recover any credits in smb_rq_done() 638 */ 639 rqp->sr_creditcharge = 0; 640 goto done; 641 } 642 643 switch (rqp->sr_command) { 644 case SMB2_NEGOTIATE: 645 case SMB2_SESSION_SETUP: 646 break; 647 648 case SMB2_LOGOFF: 649 case SMB2_ECHO: 650 case SMB2_CANCEL: 651 case SMB2_TREE_CONNECT: 652 /* these cmds always have tree id of 0 */ 653 break; 654 655 default: 656 /* If the request has a share then it has a reference on it */ 657 rqp->sr_rqtreeid = htolel(rqp->sr_share ? 658 rqp->sr_share->ss_tree_id : 659 SMB2_TID_UNKNOWN); 660 break; 661 } 662 663 /* Create the SMB2 Header */ 664 error = smb2_rq_new(rqp); 665done: 666 if (error) { 667 smb_rq_done(rqp); 668 } 669 return (error); 670} 671 672/* 673 * Returns uint32_t length of the SMB2 request 674 */ 675uint32_t 676smb2_rq_length(struct smb_rq *rqp) 677{ 678 uint32_t length; 679 680 length = (uint32_t) rqp->sr_rq.mb_len; 681 return (length); 682} 683 684/* 685 * Set Message ID in the request and increment vc_message_id by the number 686 * of credits used in the request. 687 */ 688int 689smb2_rq_message_id_increment(struct smb_rq *rqp) 690{ 691 int64_t message_id = 0; 692 struct smb_rq *tmp_rqp; 693 694 if (rqp == NULL) { 695 SMBERROR("rqp is NULL\n"); 696 return ENOMEM; 697 } 698 699 if (rqp->sr_vc == NULL) { 700 SMBERROR("rqp->sr_vc is NULL\n"); 701 return ENOMEM; 702 } 703 704 SMBC_CREDIT_LOCK(rqp->sr_vc); 705 706 if (rqp->sr_command == SMB2_NEGOTIATE) { 707 /* 708 * Negotiate is a special case where credit charge is 0. 709 * Increment message_id by 1. 710 */ 711 message_id = OSAddAtomic64(1, (int64_t*) &rqp->sr_vc->vc_message_id); 712 rqp->sr_messageid = message_id; 713 *rqp->sr_messageidp = htoleq(rqp->sr_messageid); 714 715 goto out; 716 } 717 718 if (rqp->sr_flags & SMBR_COMPOUND_RQ) { 719 /* 720 * Compound Request 721 * Set first rqp's message_id 722 * Increment message_id by same amount as the credit charge 723 */ 724 message_id = OSAddAtomic64(rqp->sr_creditcharge, 725 (int64_t*) &rqp->sr_vc->vc_message_id); 726 rqp->sr_messageid = message_id; 727 *rqp->sr_messageidp = htoleq(rqp->sr_messageid); 728 729 /* Set message_id in the other requests */ 730 tmp_rqp = rqp->sr_next_rqp; 731 while (tmp_rqp != NULL) { 732 /* Increment message_id by same amount as the credit charge */ 733 message_id = OSAddAtomic64(tmp_rqp->sr_creditcharge, 734 (int64_t*) &tmp_rqp->sr_vc->vc_message_id); 735 tmp_rqp->sr_messageid = message_id; 736 *tmp_rqp->sr_messageidp = htoleq(tmp_rqp->sr_messageid); 737 738 tmp_rqp = tmp_rqp->sr_next_rqp; 739 } 740 } 741 else { 742 /* 743 * Single Request 744 * Increment message_id by same amount as the credit charge 745 */ 746 message_id = OSAddAtomic64(rqp->sr_creditcharge, 747 (int64_t*) &rqp->sr_vc->vc_message_id); 748 rqp->sr_messageid = message_id; 749 *rqp->sr_messageidp = htoleq(rqp->sr_messageid); 750 } 751 752out: 753 SMBC_CREDIT_UNLOCK(rqp->sr_vc); 754 755 return 0; 756} 757 758/* 759 * next_cmd_offset is the total of all next_cmd_offset offsets. Subtract the 760 * bytes that we have parsed and that should leave the number of pad bytes to 761 * consume. 762 */ 763int 764smb2_rq_next_command(struct smb_rq *rqp, size_t *next_cmd_offset, 765 struct mdchain *mdp) 766{ 767 ssize_t pad_bytes = 0; 768 int error = 0; 769 770 *next_cmd_offset += rqp->sr_rspnextcmd; 771 772 /* take total of next_cmd_offset and subtract what we have parsed */ 773 pad_bytes = *next_cmd_offset - mdp->md_len; 774 775 if (pad_bytes > 0) { 776 error = md_get_mem(mdp, NULL, pad_bytes, MB_MSYSTEM); 777 if (error) { 778 SMBERROR("md_get_mem failed %d\n", error); 779 } 780 } 781 return(error); 782} 783 784static int 785smb2_rq_new(struct smb_rq *rqp) 786{ 787 struct mbchain *mbp; 788 struct mdchain *mdp; 789 int error; 790 791 smb_rq_getrequest(rqp, &mbp); 792 smb_rq_getreply(rqp, &mdp); 793 794 mb_done(mbp); 795 md_done(mdp); 796 error = mb_init(mbp); 797 if (error) { 798 return error; 799 } 800 801 if (!(rqp->sr_flags & SMBR_ASYNC)) { 802 /* 803 * Build SMB2 Sync Header 804 */ 805 mb_put_mem(mbp, SMB2_SIGNATURE, SMB2_SIGLEN, MB_MSYSTEM); /* Protocol ID */ 806 mb_put_uint16le(mbp, SMB2_HDRLEN); /* Struct Size */ 807 rqp->sr_creditchargep = (uint16_t *)mb_reserve(mbp, 2); /* Credit Charge */ 808 *rqp->sr_creditchargep = htoles(rqp->sr_creditcharge); 809 mb_put_uint32le(mbp, 0); /* Status */ 810 mb_put_uint16le(mbp, rqp->sr_command); /* Command */ 811 rqp->sr_creditreqp = (uint16_t *)mb_reserve(mbp, 2); /* Credit Req/Rsp */ 812 *rqp->sr_creditreqp = htoles(rqp->sr_creditsrequested); 813 rqp->sr_flagsp = (uint32_t *)mb_reserve(mbp, 4); /* Flags */ 814 *rqp->sr_flagsp = htolel(rqp->sr_rqflags); 815 rqp->sr_nextcmdp = (uint32_t *)mb_reserve(mbp, 4); /* Next command */ 816 *rqp->sr_nextcmdp = htolel(rqp->sr_nextcmd); 817 rqp->sr_messageidp = (uint64_t *)mb_reserve(mbp, 8); /* Message ID */ 818 bzero(rqp->sr_messageidp, 8); 819 mb_put_uint32le(mbp, 0xFEFF); /* Process ID */ 820 mb_put_uint32le(mbp, rqp->sr_rqtreeid); /* Tree ID */ 821 mb_put_uint64le(mbp, rqp->sr_rqsessionid); /* Session ID */ 822 rqp->sr_rqsig = (uint8_t *)mb_reserve(mbp, 16); /* Signature */ 823 bzero(rqp->sr_rqsig, 16); 824 } 825 else { 826 /* 827 * Build SMB2 Async Header 828 */ 829 mb_put_mem(mbp, SMB2_SIGNATURE, SMB2_SIGLEN, MB_MSYSTEM); /* Protocol ID */ 830 mb_put_uint16le(mbp, SMB2_HDRLEN); /* Struct Size */ 831 rqp->sr_creditchargep = (uint16_t *)mb_reserve(mbp, 2); /* Credit Charge */ 832 *rqp->sr_creditchargep = htoles(rqp->sr_creditcharge); 833 mb_put_uint32le(mbp, 0); /* Status */ 834 mb_put_uint16le(mbp, rqp->sr_command); /* Command */ 835 rqp->sr_creditreqp = (uint16_t *)mb_reserve(mbp, 2); /* Credit Req/Rsp */ 836 *rqp->sr_creditreqp = htoles(rqp->sr_creditsrequested); 837 rqp->sr_flagsp = (uint32_t *)mb_reserve(mbp, 4); /* Flags */ 838 rqp->sr_rqflags |= SMB2_FLAGS_ASYNC_COMMAND; 839 *rqp->sr_flagsp = htolel(rqp->sr_rqflags); 840 rqp->sr_nextcmdp = (uint32_t *)mb_reserve(mbp, 4); /* Next command */ 841 *rqp->sr_nextcmdp = htolel(rqp->sr_nextcmd); 842 rqp->sr_messageidp = (uint64_t *)mb_reserve(mbp, 8); /* Message ID */ 843 bzero(rqp->sr_messageidp, 8); 844 mb_put_uint64le(mbp, 0); /* Async ID */ 845 mb_put_uint64le(mbp, rqp->sr_rqsessionid); /* Session ID */ 846 rqp->sr_rqsig = (uint8_t *)mb_reserve(mbp, 16); /* Signature */ 847 bzero(rqp->sr_rqsig, 16); 848 } 849 850 return 0; 851} 852 853/* 854 * Parses SMB2 Response Header 855 * For non compound responses, the mdp is from the rqp. 856 * For compound responses, the mdp may be from the first rqp in the chain and 857 * not from the rqp passed into this function. 858 */ 859uint32_t 860smb2_rq_parse_header(struct smb_rq *rqp, struct mdchain **mdp) 861{ 862 int error = 0, rperror = 0; 863 uint32_t protocol_id; 864 uint16_t length, credit_charge, command; 865 uint64_t message_id = 0; 866 uint64_t async_id = 0; 867 struct mdchain md_sign; 868 uint8_t signature[16]; 869 870 /* 871 * Parse SMB2 Header 872 * We are already pointing to begining of header data 873 */ 874 875 if (rqp == NULL) { 876 SMBERROR("rqp is NULL\n"); 877 error = ENOMEM; 878 goto bad; 879 } 880 881 /* 882 * <14227703> If SMB2_RESPONSE is set, then the reply is in the rqp 883 * Must be a server that does not support compound replies 884 */ 885 if (rqp->sr_extflags & SMB2_RESPONSE) { 886 /* Get pointer to response data */ 887 smb_rq_getreply(rqp, mdp); 888 } 889 890 /* 891 * Hold on to a copy of the mdchain before we touch it, 892 * since we will need to verify the signature 893 * if signing is turned on 894 */ 895 md_sign = **mdp; 896 897 /* Get Protocol ID */ 898 error = md_get_uint32le(*mdp, &protocol_id); 899 if (error) { 900 goto bad; 901 } 902 903 /* Check structure size is 64 */ 904 error = md_get_uint16le(*mdp, &length); 905 if (error) { 906 goto bad; 907 } 908 if (length != 64) { 909 SMBERROR("Bad struct size: %u\n", (uint32_t) length); 910 error = EBADRPC; 911 goto bad; 912 } 913 914 /* Get Credit Charge */ 915 error = md_get_uint16le(*mdp, &credit_charge); 916 if (error) { 917 goto bad; 918 } 919 920 /* Get Status */ 921 error = md_get_uint32le(*mdp, &rqp->sr_ntstatus); 922 if (error) { 923 goto bad; 924 } 925 926 /* Get Command */ 927 error = md_get_uint16le(*mdp, &command); 928 if (error) { 929 goto bad; 930 } 931 932 /* Get Credits Granted */ 933 error = md_get_uint16le(*mdp, &rqp->sr_rspcreditsgranted); 934 if (error) { 935 goto bad; 936 } 937 938 /* Increment current credits granted */ 939 smb2_rq_credit_increment(rqp); 940 941 /* Get Flags */ 942 error = md_get_uint32le(*mdp, &rqp->sr_rspflags); 943 if (error) { 944 goto bad; 945 } 946 947 /* Get Next Command offset */ 948 error = md_get_uint32le(*mdp, &rqp->sr_rspnextcmd); 949 if (error) { 950 goto bad; 951 } 952 953 /* Get Message ID */ 954 error = md_get_uint64le(*mdp, &message_id); 955 if (error) { 956 goto bad; 957 } 958 959 if (!(rqp->sr_rspflags & SMB2_FLAGS_ASYNC_COMMAND)) { 960 /* 961 * Sync Header 962 */ 963 964 /* Get Process ID */ 965 error = md_get_uint32le(*mdp, &rqp->sr_rsppid); 966 if (error) { 967 goto bad; 968 } 969 970 /* Get Tree ID */ 971 error = md_get_uint32le(*mdp, &rqp->sr_rsptreeid); 972 if (error) { 973 goto bad; 974 } 975 } 976 else { 977 /* 978 * Async Header 979 */ 980 981 /* Get Async ID */ 982 error = md_get_uint64le(*mdp, &async_id); 983 if (error) { 984 goto bad; 985 } 986 987 if (async_id != rqp->sr_rspasyncid) { 988 SMBERROR("Async rsp ids do not match: id %lld async_id %lld ! = %lld\n", 989 message_id, async_id, rqp->sr_rspasyncid); 990 error = EBADRPC; 991 goto bad; 992 } 993 } 994 995 /* Get Session ID */ 996 error = md_get_uint64le(*mdp, &rqp->sr_rspsessionid); 997 if (error) { 998 goto bad; 999 } 1000 1001 /* Get Signature */ 1002 error = md_get_mem(*mdp, (caddr_t) &signature, sizeof(signature), MB_MSYSTEM); 1003 if (error) { 1004 goto bad; 1005 } 1006 1007 /* If it's signed, then verify the signature */ 1008 if ( (error == 0) && 1009 (rqp->sr_vc->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE)) { 1010 error = smb2_rq_verify(rqp, &md_sign, signature); 1011 } 1012 1013 /* 1014 * If the signature failed verification, do not bother with 1015 * looking at sr_ntstatus, just punt. 1016 */ 1017 if (error) 1018 goto bad; 1019 1020 /* Convert NT Status to an errno value */ 1021 rperror = smb_ntstatus_to_errno(rqp->sr_ntstatus); 1022 1023 switch (rqp->sr_ntstatus) { 1024 case STATUS_INSUFFICIENT_RESOURCES: 1025 SMBWARNING("STATUS_INSUFFICIENT_RESOURCES: while attempting cmd %x\n", 1026 rqp->sr_cmd); 1027 break; 1028 1029 case STATUS_NETWORK_SESSION_EXPIRED: 1030 if (rqp->sr_context == rqp->sr_vc->vc_iod->iod_context) { 1031 /* 1032 * Its a reconnect command so dont recurse into reconnect 1033 * again. Just fail reconnect. 1034 */ 1035 SMBWARNING("STATUS_NETWORK_SESSION_EXPIRED: while reconnecting cmd %x. Disconnecting.\n", 1036 rqp->sr_cmd); 1037 rperror = ENETRESET; 1038 } 1039 else { 1040 SMBWARNING("STATUS_NETWORK_SESSION_EXPIRED: while attempting cmd %x. Reconnecting.\n", 1041 rqp->sr_cmd); 1042 1043 (void) smb_vc_force_reconnect(rqp->sr_vc); 1044 } 1045 break; 1046 1047 default: 1048 break; 1049 } 1050 1051 /* The tree has gone away, umount the volume. */ 1052 if ((rperror == ENETRESET) && rqp->sr_share) { 1053 lck_mtx_lock(&rqp->sr_share->ss_shlock); 1054 if ( rqp->sr_share->ss_dead) 1055 rqp->sr_share->ss_dead(rqp->sr_share); 1056 lck_mtx_unlock(&rqp->sr_share->ss_shlock); 1057 } 1058 1059 /* Need bigger buffer? */ 1060 if (rperror && (rqp->sr_ntstatus == STATUS_BUFFER_TOO_SMALL)) { 1061 rqp->sr_flags |= SMBR_MOREDATA; 1062 } else { 1063 rqp->sr_flags &= ~SMBR_MOREDATA; 1064 } 1065 1066bad: 1067 return error ? error : rperror; 1068} 1069 1070/* 1071 * Sets SMB2 Header Flags and NextCommand for Compound req chains 1072 */ 1073int 1074smb2_rq_update_cmpd_hdr(struct smb_rq *rqp, uint32_t position_flag) 1075{ 1076 if (rqp == NULL) 1077 return ENOMEM; 1078 1079 if (rqp->sr_nextcmdp == NULL) 1080 return ENOMEM; 1081 1082 if (rqp->sr_flagsp == NULL) 1083 return ENOMEM; 1084 1085 switch (position_flag) { 1086 case SMB2_CMPD_FIRST: 1087 /* 1088 * Do not set SMB2_FLAGS_RELATED_OPERATIONS 1089 * Set next command offset 1090 */ 1091 *rqp->sr_nextcmdp = htolel(smb2_rq_length(rqp)); 1092 break; 1093 case SMB2_CMPD_MIDDLE: 1094 /* 1095 * Set SMB2_FLAGS_RELATED_OPERATIONS 1096 * Set next command offset 1097 */ 1098 *rqp->sr_nextcmdp = htolel(smb2_rq_length(rqp)); 1099 *rqp->sr_flagsp |= htolel(SMB2_FLAGS_RELATED_OPERATIONS); 1100 break; 1101 case SMB2_CMPD_LAST: 1102 /* 1103 * Set SMB2_FLAGS_RELATED_OPERATIONS 1104 * Set next command offset to be 0 1105 */ 1106 *rqp->sr_nextcmdp = htolel(0); 1107 *rqp->sr_flagsp |= htolel(SMB2_FLAGS_RELATED_OPERATIONS); 1108 break; 1109 1110 default: 1111 SMBERROR("Unknown postion_flag %d\n", position_flag); 1112 return ENOMEM; 1113 break; 1114 } 1115 1116 return 0; 1117} 1118 1119 1120 1121 1122 1123