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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2018 Nexenta Systems, Inc. All rights reserved. 26 * Copyright 2021 RackTop Systems, Inc. 27 */ 28 29 #include <smbsrv/smb_kproto.h> 30 #include <smbsrv/smb_dfs.h> 31 #include <smbsrv/smb_door.h> 32 #include <smb/winioctl.h> 33 34 /* 35 * Get Referral response header flags 36 * For exact meaning refer to MS-DFSC spec. 37 * 38 * R: ReferralServers 39 * S: StorageServers 40 * T: TargetFailback 41 */ 42 #define DFS_HDRFLG_R 0x00000001 43 #define DFS_HDRFLG_S 0x00000002 44 #define DFS_HDRFLG_T 0x00000004 45 46 /* 47 * Entry flags 48 */ 49 #define DFS_ENTFLG_T 0x0004 50 51 /* 52 * Referral entry types/versions 53 */ 54 #define DFS_REFERRAL_V1 0x0001 55 #define DFS_REFERRAL_V2 0x0002 56 #define DFS_REFERRAL_V3 0x0003 57 #define DFS_REFERRAL_V4 0x0004 58 59 /* 60 * Valid values for ServerType field in referral entries 61 */ 62 #define DFS_SRVTYPE_NONROOT 0x0000 63 #define DFS_SRVTYPE_ROOT 0x0001 64 65 /* 66 * Size of the fix part for each referral entry type 67 */ 68 #define DFS_REFV1_ENTSZ 8 69 #define DFS_REFV2_ENTSZ 22 70 #define DFS_REFV3_ENTSZ 34 71 #define DFS_REFV4_ENTSZ 34 72 73 static dfs_reftype_t smb_dfs_get_reftype(const char *); 74 static void smb_dfs_encode_hdr(mbuf_chain_t *, dfs_info_t *); 75 static uint32_t smb_dfs_encode_refv1(smb_request_t *, mbuf_chain_t *, 76 dfs_info_t *); 77 static uint32_t smb_dfs_encode_refv2(smb_request_t *, mbuf_chain_t *, 78 dfs_info_t *); 79 static uint32_t smb_dfs_encode_refv3x(smb_request_t *, mbuf_chain_t *, 80 dfs_info_t *, uint16_t); 81 static void smb_dfs_encode_targets(mbuf_chain_t *, dfs_info_t *); 82 static uint32_t smb_dfs_referrals_get(smb_request_t *, char *, dfs_reftype_t, 83 dfs_referral_response_t *); 84 static void smb_dfs_referrals_free(dfs_referral_response_t *); 85 static uint16_t smb_dfs_referrals_unclen(dfs_info_t *, uint16_t); 86 static uint32_t smb_dfs_get_referrals_ex(smb_request_t *, smb_fsctl_t *); 87 88 /* 89 * Handle device type FILE_DEVICE_DFS 90 * for smb2_ioctl 91 */ 92 uint32_t 93 smb_dfs_fsctl(smb_request_t *sr, smb_fsctl_t *fsctl) 94 { 95 uint32_t status; 96 97 if (!STYPE_ISIPC(sr->tid_tree->t_res_type)) 98 return (NT_STATUS_INVALID_DEVICE_REQUEST); 99 100 /* 101 * If the connection is not DFS capable, we should return 102 * NT_STATUS_FS_DRIVER_REQUIRED for both of these DFS ioctls. 103 * See [MS-SMB2] 3.3.5.15.2. 104 */ 105 if ((sr->session->srv_cap & SMB2_CAP_DFS) == 0) 106 return (NT_STATUS_FS_DRIVER_REQUIRED); 107 108 switch (fsctl->CtlCode) { 109 case FSCTL_DFS_GET_REFERRALS: 110 status = smb_dfs_get_referrals(sr, fsctl); 111 break; 112 case FSCTL_DFS_GET_REFERRALS_EX: 113 status = smb_dfs_get_referrals_ex(sr, fsctl); 114 break; 115 default: 116 /* 117 * MS-SMB2 suggests INVALID_DEVICE_REQUEST 118 * for unknown control codes, but using that 119 * here makes Windows unhappy. 120 */ 121 status = NT_STATUS_FS_DRIVER_REQUIRED; 122 } 123 124 return (status); 125 } 126 127 /* 128 * XXX Instead of decoding the referral request and encoding 129 * the response here (in-kernel) we could pass the given 130 * request buffer in our door call, and let that return the 131 * response buffer ready to stuff into out_mbc. That would 132 * allow all this decoding/encoding to happen at user-level. 133 * (and most of this file would go away. :-) 134 */ 135 136 /* 137 * See [MS-DFSC] for details about this command 138 * Handles FSCTL_DFS_GET_REFERRALS_EX (only) 139 */ 140 uint32_t 141 smb_dfs_get_referrals_ex(smb_request_t *sr, smb_fsctl_t *fsctl) 142 { 143 dfs_info_t *referrals; 144 dfs_referral_response_t refrsp; 145 dfs_reftype_t reftype; 146 char *path; 147 uint16_t maxver; 148 uint16_t flags; 149 uint16_t fnlen; 150 uint32_t datalen; 151 uint32_t status; 152 int rc; 153 154 /* 155 * The caller checks this, because the error reporting method 156 * varies across SMB versions. 157 */ 158 ASSERT(STYPE_ISIPC(sr->tid_tree->t_res_type)); 159 160 /* 161 * Decode fixed part. 162 * Input data is (w) MaxReferralLevel, (w) Flags, 163 * (l) RequestDataLength, ... variable data ... 164 */ 165 rc = smb_mbc_decodef(fsctl->in_mbc, "wwl", 166 &maxver, &flags, &datalen); 167 if (rc != 0) 168 return (NT_STATUS_INVALID_PARAMETER); 169 170 /* 171 * Decode variable part: 172 * (w) file name length, (u) filename, 173 * ( if flags & 1 ) 174 * (w) site name len, (u) site name 175 * We don't decode or use the site name 176 */ 177 if (MBC_ROOM_FOR(fsctl->in_mbc, datalen) == 0) 178 return (NT_STATUS_INVALID_PARAMETER); 179 rc = smb_mbc_decodef(fsctl->in_mbc, "%wu", 180 sr, &fnlen, &path); 181 if (rc != 0) 182 return (NT_STATUS_INVALID_PARAMETER); 183 184 reftype = smb_dfs_get_reftype((const char *)path); 185 switch (reftype) { 186 case DFS_REFERRAL_INVALID: 187 /* Need to check the error for this case */ 188 return (NT_STATUS_INVALID_PARAMETER); 189 190 case DFS_REFERRAL_DOMAIN: 191 case DFS_REFERRAL_DC: 192 /* MS-DFSC: this error is returned by non-DC root */ 193 return (NT_STATUS_INVALID_PARAMETER); 194 195 case DFS_REFERRAL_SYSVOL: 196 /* MS-DFSC: this error is returned by non-DC root */ 197 return (NT_STATUS_NO_SUCH_DEVICE); 198 199 default: 200 break; 201 } 202 203 status = smb_dfs_referrals_get(sr, path, reftype, &refrsp); 204 if (status != NT_STATUS_SUCCESS) 205 return (status); 206 207 referrals = &refrsp.rp_referrals; 208 smb_dfs_encode_hdr(fsctl->out_mbc, referrals); 209 210 /* 211 * Server may respond with any referral version at or below 212 * the maximum specified in the request. 213 */ 214 switch (maxver) { 215 case DFS_REFERRAL_V1: 216 status = smb_dfs_encode_refv1(sr, fsctl->out_mbc, referrals); 217 break; 218 219 case DFS_REFERRAL_V2: 220 status = smb_dfs_encode_refv2(sr, fsctl->out_mbc, referrals); 221 break; 222 223 case DFS_REFERRAL_V3: 224 status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals, 225 DFS_REFERRAL_V3); 226 break; 227 228 case DFS_REFERRAL_V4: 229 default: 230 status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals, 231 DFS_REFERRAL_V4); 232 break; 233 } 234 235 smb_dfs_referrals_free(&refrsp); 236 237 return (status); 238 } 239 240 /* 241 * Note: SMB1 callers in smb_trans2_dfs.c 242 * smb_com_trans2_report_dfs_inconsistency 243 * smb_com_trans2_get_dfs_referral 244 */ 245 246 /* 247 * See [MS-DFSC] for details about this command 248 * Handles FSCTL_DFS_GET_REFERRALS (only) 249 */ 250 uint32_t 251 smb_dfs_get_referrals(smb_request_t *sr, smb_fsctl_t *fsctl) 252 { 253 dfs_info_t *referrals; 254 dfs_referral_response_t refrsp; 255 dfs_reftype_t reftype; 256 char *path; 257 uint16_t maxver; 258 uint32_t status; 259 int rc; 260 261 /* 262 * The caller checks this, because the error reporting method 263 * varies across SMB versions. 264 */ 265 ASSERT(STYPE_ISIPC(sr->tid_tree->t_res_type)); 266 267 /* 268 * Input data is (w) MaxReferralLevel, (U) path 269 */ 270 rc = smb_mbc_decodef(fsctl->in_mbc, "%wu", 271 sr, &maxver, &path); 272 if (rc != 0) 273 return (NT_STATUS_INVALID_PARAMETER); 274 275 reftype = smb_dfs_get_reftype((const char *)path); 276 switch (reftype) { 277 case DFS_REFERRAL_INVALID: 278 /* Need to check the error for this case */ 279 return (NT_STATUS_INVALID_PARAMETER); 280 281 case DFS_REFERRAL_DOMAIN: 282 case DFS_REFERRAL_DC: 283 /* MS-DFSC: this error is returned by non-DC root */ 284 return (NT_STATUS_INVALID_PARAMETER); 285 286 case DFS_REFERRAL_SYSVOL: 287 /* MS-DFSC: this error is returned by non-DC root */ 288 return (NT_STATUS_NO_SUCH_DEVICE); 289 290 default: 291 break; 292 } 293 294 status = smb_dfs_referrals_get(sr, path, reftype, &refrsp); 295 if (status != NT_STATUS_SUCCESS) 296 return (status); 297 298 referrals = &refrsp.rp_referrals; 299 smb_dfs_encode_hdr(fsctl->out_mbc, referrals); 300 301 /* 302 * Server may respond with any referral version at or below 303 * the maximum specified in the request. 304 */ 305 switch (maxver) { 306 case DFS_REFERRAL_V1: 307 status = smb_dfs_encode_refv1(sr, fsctl->out_mbc, referrals); 308 break; 309 310 case DFS_REFERRAL_V2: 311 status = smb_dfs_encode_refv2(sr, fsctl->out_mbc, referrals); 312 break; 313 314 case DFS_REFERRAL_V3: 315 status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals, 316 DFS_REFERRAL_V3); 317 break; 318 319 case DFS_REFERRAL_V4: 320 default: 321 status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals, 322 DFS_REFERRAL_V4); 323 break; 324 } 325 326 smb_dfs_referrals_free(&refrsp); 327 328 return (status); 329 } 330 331 /* 332 * [MS-DFSC]: REQ_GET_DFS_REFERRAL 333 * 334 * Determines the referral type based on the specified path: 335 * 336 * Domain referral: 337 * "" 338 * 339 * DC referral: 340 * \<domain> 341 * 342 * Sysvol referral: 343 * \<domain>\SYSVOL 344 * \<domain>\NETLOGON 345 * 346 * Root referral: 347 * \<domain>\<dfsname> 348 * \<server>\<dfsname> 349 * 350 * Link referral: 351 * \<domain>\<dfsname>\<linkpath> 352 * \<server>\<dfsname>\<linkpath> 353 */ 354 static dfs_reftype_t 355 smb_dfs_get_reftype(const char *path) 356 { 357 smb_unc_t unc; 358 dfs_reftype_t reftype = 0; 359 360 if (*path == '\0') 361 return (DFS_REFERRAL_DOMAIN); 362 363 if (smb_unc_init(path, &unc) != 0) 364 return (DFS_REFERRAL_INVALID); 365 366 if (unc.unc_path != NULL) { 367 reftype = DFS_REFERRAL_LINK; 368 } else if (unc.unc_share != NULL) { 369 if ((smb_strcasecmp(unc.unc_share, "SYSVOL", 0) == 0) || 370 (smb_strcasecmp(unc.unc_share, "NETLOGON", 0) == 0)) { 371 reftype = DFS_REFERRAL_SYSVOL; 372 } else { 373 reftype = DFS_REFERRAL_ROOT; 374 } 375 } else if (unc.unc_server != NULL) { 376 reftype = DFS_REFERRAL_DC; 377 } 378 379 smb_unc_free(&unc); 380 return (reftype); 381 } 382 383 static void 384 smb_dfs_encode_hdr(mbuf_chain_t *mbc, dfs_info_t *referrals) 385 { 386 uint16_t path_consumed; 387 uint32_t flags; 388 389 path_consumed = smb_wcequiv_strlen(referrals->i_uncpath); 390 flags = DFS_HDRFLG_S; 391 if (referrals->i_type == DFS_OBJECT_ROOT) 392 flags |= DFS_HDRFLG_R; 393 394 /* Fill rep_param_mb in SMB1 caller. */ 395 (void) smb_mbc_encodef(mbc, "wwl", path_consumed, 396 referrals->i_ntargets, flags); 397 } 398 399 static uint32_t 400 smb_dfs_encode_refv1(smb_request_t *sr, mbuf_chain_t *mbc, 401 dfs_info_t *referrals) 402 { 403 _NOTE(ARGUNUSED(sr)) 404 uint16_t entsize, rep_bufsize; 405 uint16_t server_type; 406 uint16_t flags = 0; 407 uint16_t r; 408 char *target; 409 410 rep_bufsize = MBC_MAXBYTES(mbc); 411 412 server_type = (referrals->i_type == DFS_OBJECT_ROOT) ? 413 DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT; 414 415 target = kmem_alloc(MAXPATHLEN, KM_SLEEP); 416 417 for (r = 0; r < referrals->i_ntargets; r++) { 418 (void) snprintf(target, MAXPATHLEN, "\\%s\\%s", 419 referrals->i_targets[r].t_server, 420 referrals->i_targets[r].t_share); 421 422 entsize = DFS_REFV1_ENTSZ + smb_wcequiv_strlen(target) + 2; 423 if (entsize > rep_bufsize) 424 break; 425 426 (void) smb_mbc_encodef(mbc, "wwwwU", 427 DFS_REFERRAL_V1, entsize, server_type, flags, target); 428 rep_bufsize -= entsize; 429 } 430 431 kmem_free(target, MAXPATHLEN); 432 433 /* 434 * Need room for at least one entry. 435 * Windows will silently drop targets that do not fit in 436 * the response buffer. 437 */ 438 if (r == 0) { 439 return (NT_STATUS_BUFFER_OVERFLOW); 440 } 441 442 return (NT_STATUS_SUCCESS); 443 } 444 445 /* 446 * Prepare a response with V2 referral format. 447 * 448 * Here is the response packet format. 449 * All the strings come after all the fixed size entry headers. 450 * These headers contain offsets to the strings at the end. Note 451 * that the two "dfs_path" after the last entry is shared between 452 * all the entries. 453 * 454 * ent1-hdr 455 * ent2-hdr 456 * ... 457 * entN-hdr 458 * dfs_path 459 * dfs_path 460 * target1 461 * target2 462 * ... 463 * targetN 464 * 465 * MS-DFSC mentions that strings can come after each entry header or all after 466 * the last entry header. Windows responses are in the format above. 467 */ 468 static uint32_t 469 smb_dfs_encode_refv2(smb_request_t *sr, mbuf_chain_t *mbc, 470 dfs_info_t *referrals) 471 { 472 _NOTE(ARGUNUSED(sr)) 473 uint16_t entsize, rep_bufsize; 474 uint16_t server_type; 475 uint16_t flags = 0; 476 uint32_t proximity = 0; 477 uint16_t path_offs, altpath_offs, netpath_offs; 478 uint16_t targetsz, total_targetsz = 0; 479 uint16_t dfs_pathsz; 480 uint16_t r; 481 482 rep_bufsize = MBC_MAXBYTES(mbc); 483 dfs_pathsz = smb_wcequiv_strlen(referrals->i_uncpath) + 2; 484 entsize = DFS_REFV2_ENTSZ + dfs_pathsz + dfs_pathsz + 485 smb_dfs_referrals_unclen(referrals, 0); 486 487 if (entsize > rep_bufsize) { 488 /* need room for at least one referral */ 489 return (NT_STATUS_BUFFER_OVERFLOW); 490 } 491 492 server_type = (referrals->i_type == DFS_OBJECT_ROOT) ? 493 DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT; 494 495 rep_bufsize -= entsize; 496 entsize = DFS_REFV2_ENTSZ; 497 498 for (r = 0; r < referrals->i_ntargets; r++) { 499 path_offs = (referrals->i_ntargets - r) * DFS_REFV2_ENTSZ; 500 altpath_offs = path_offs + dfs_pathsz; 501 netpath_offs = altpath_offs + dfs_pathsz + total_targetsz; 502 targetsz = smb_dfs_referrals_unclen(referrals, r); 503 504 if (r != 0) { 505 entsize = DFS_REFV2_ENTSZ + targetsz; 506 if (entsize > rep_bufsize) 507 /* silently drop targets that do not fit */ 508 break; 509 rep_bufsize -= entsize; 510 } 511 512 (void) smb_mbc_encodef(mbc, "wwwwllwww", 513 DFS_REFERRAL_V2, DFS_REFV2_ENTSZ, server_type, flags, 514 proximity, referrals->i_timeout, path_offs, altpath_offs, 515 netpath_offs); 516 517 total_targetsz += targetsz; 518 } 519 520 smb_dfs_encode_targets(mbc, referrals); 521 522 return (NT_STATUS_SUCCESS); 523 } 524 525 /* 526 * Prepare a response with V3/V4 referral format. 527 * 528 * For more details, see comments for smb_dfs_encode_refv2() or see 529 * MS-DFSC specification. 530 */ 531 static uint32_t 532 smb_dfs_encode_refv3x(smb_request_t *sr, mbuf_chain_t *mbc, 533 dfs_info_t *referrals, uint16_t ver) 534 { 535 _NOTE(ARGUNUSED(sr)) 536 uint16_t entsize, rep_bufsize, hdrsize; 537 uint16_t server_type; 538 uint16_t flags = 0; 539 uint16_t path_offs, altpath_offs, netpath_offs; 540 uint16_t targetsz, total_targetsz = 0; 541 uint16_t dfs_pathsz; 542 uint16_t r; 543 544 hdrsize = (ver == DFS_REFERRAL_V3) ? DFS_REFV3_ENTSZ : DFS_REFV4_ENTSZ; 545 rep_bufsize = MBC_MAXBYTES(mbc); 546 dfs_pathsz = smb_wcequiv_strlen(referrals->i_uncpath) + 2; 547 entsize = hdrsize + dfs_pathsz + dfs_pathsz + 548 smb_dfs_referrals_unclen(referrals, 0); 549 550 if (entsize > rep_bufsize) { 551 /* need room for at least one referral */ 552 return (NT_STATUS_BUFFER_OVERFLOW); 553 } 554 555 server_type = (referrals->i_type == DFS_OBJECT_ROOT) ? 556 DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT; 557 558 rep_bufsize -= entsize; 559 560 for (r = 0; r < referrals->i_ntargets; r++) { 561 path_offs = (referrals->i_ntargets - r) * hdrsize; 562 altpath_offs = path_offs + dfs_pathsz; 563 netpath_offs = altpath_offs + dfs_pathsz + total_targetsz; 564 targetsz = smb_dfs_referrals_unclen(referrals, r); 565 566 if (r != 0) { 567 entsize = hdrsize + targetsz; 568 if (entsize > rep_bufsize) 569 /* silently drop targets that do not fit */ 570 break; 571 rep_bufsize -= entsize; 572 flags = 0; 573 } else if (ver == DFS_REFERRAL_V4) { 574 flags = DFS_ENTFLG_T; 575 } 576 577 (void) smb_mbc_encodef(mbc, "wwwwlwww16.", 578 ver, hdrsize, server_type, flags, 579 referrals->i_timeout, path_offs, altpath_offs, 580 netpath_offs); 581 582 total_targetsz += targetsz; 583 } 584 585 smb_dfs_encode_targets(mbc, referrals); 586 587 return (NT_STATUS_SUCCESS); 588 } 589 590 /* 591 * Encodes DFS path, and target strings which come after fixed header 592 * entries. 593 * 594 * Windows 2000 and earlier set the DFSAlternatePathOffset to point to 595 * an 8.3 string representation of the string pointed to by 596 * DFSPathOffset if it is not a legal 8.3 string. Otherwise, if 597 * DFSPathOffset points to a legal 8.3 string, DFSAlternatePathOffset 598 * points to a separate copy of the same string. Windows Server 2003, 599 * Windows Server 2008 and Windows Server 2008 R2 set the 600 * DFSPathOffset and DFSAlternatePathOffset fields to point to separate 601 * copies of the identical string. 602 * 603 * Following Windows 2003 and later here. 604 */ 605 static void 606 smb_dfs_encode_targets(mbuf_chain_t *mbc, dfs_info_t *referrals) 607 { 608 char *target; 609 int r; 610 611 (void) smb_mbc_encodef(mbc, "UU", referrals->i_uncpath, 612 referrals->i_uncpath); 613 614 target = kmem_alloc(MAXPATHLEN, KM_SLEEP); 615 for (r = 0; r < referrals->i_ntargets; r++) { 616 (void) snprintf(target, MAXPATHLEN, "\\%s\\%s", 617 referrals->i_targets[r].t_server, 618 referrals->i_targets[r].t_share); 619 (void) smb_mbc_encodef(mbc, "U", target); 620 } 621 kmem_free(target, MAXPATHLEN); 622 } 623 624 /* 625 * Get referral information for the specified path from user space 626 * using a door call. 627 */ 628 static uint32_t 629 smb_dfs_referrals_get(smb_request_t *sr, char *dfs_path, dfs_reftype_t reftype, 630 dfs_referral_response_t *refrsp) 631 { 632 dfs_referral_query_t req; 633 int rc; 634 635 req.rq_type = reftype; 636 req.rq_path = dfs_path; 637 638 bzero(refrsp, sizeof (dfs_referral_response_t)); 639 refrsp->rp_status = NT_STATUS_NOT_FOUND; 640 641 rc = smb_kdoor_upcall(sr->sr_server, SMB_DR_DFS_GET_REFERRALS, 642 &req, dfs_referral_query_xdr, refrsp, dfs_referral_response_xdr); 643 644 if (rc != 0) 645 return (NT_STATUS_FS_DRIVER_REQUIRED); 646 647 /* 648 * Map the Win error to one of the NT status codes 649 * documented in MS-DFSC. The most common, when we 650 * have no DFS root configured, is NOT_FOUND. 651 */ 652 switch (refrsp->rp_status) { 653 case ERROR_SUCCESS: 654 break; 655 case ERROR_INVALID_PARAMETER: 656 return (NT_STATUS_INVALID_PARAMETER); 657 case ERROR_NOT_ENOUGH_MEMORY: 658 return (NT_STATUS_INSUFFICIENT_RESOURCES); 659 case ERROR_NOT_FOUND: 660 return (NT_STATUS_NOT_FOUND); 661 default: 662 return (NT_STATUS_UNEXPECTED_NETWORK_ERROR); 663 } 664 665 (void) strsubst(refrsp->rp_referrals.i_uncpath, '/', '\\'); 666 return (NT_STATUS_SUCCESS); 667 } 668 669 static void 670 smb_dfs_referrals_free(dfs_referral_response_t *refrsp) 671 { 672 xdr_free(dfs_referral_response_xdr, (char *)refrsp); 673 } 674 675 /* 676 * Returns the Unicode string length for the target UNC of 677 * the specified entry by 'refno' 678 * 679 * Note that the UNC path should be encoded with ONE leading 680 * slash not two as is common to user-visible UNC paths. 681 */ 682 static uint16_t 683 smb_dfs_referrals_unclen(dfs_info_t *referrals, uint16_t refno) 684 { 685 uint16_t len; 686 687 if (refno >= referrals->i_ntargets) 688 return (0); 689 690 /* Encoded target UNC \server\share */ 691 len = smb_wcequiv_strlen(referrals->i_targets[refno].t_server) + 692 smb_wcequiv_strlen(referrals->i_targets[refno].t_share) + 693 smb_wcequiv_strlen("\\\\") + 2; /* two '\' + NULL */ 694 695 return (len); 696 } 697