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
smb_dfs_fsctl(smb_request_t * sr,smb_fsctl_t * fsctl)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
smb_dfs_get_referrals_ex(smb_request_t * sr,smb_fsctl_t * fsctl)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
smb_dfs_get_referrals(smb_request_t * sr,smb_fsctl_t * fsctl)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
smb_dfs_get_reftype(const char * path)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
smb_dfs_encode_hdr(mbuf_chain_t * mbc,dfs_info_t * referrals)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
smb_dfs_encode_refv1(smb_request_t * sr,mbuf_chain_t * mbc,dfs_info_t * referrals)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
smb_dfs_encode_refv2(smb_request_t * sr,mbuf_chain_t * mbc,dfs_info_t * referrals)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
smb_dfs_encode_refv3x(smb_request_t * sr,mbuf_chain_t * mbc,dfs_info_t * referrals,uint16_t ver)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
smb_dfs_encode_targets(mbuf_chain_t * mbc,dfs_info_t * referrals)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
smb_dfs_referrals_get(smb_request_t * sr,char * dfs_path,dfs_reftype_t reftype,dfs_referral_response_t * refrsp)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
smb_dfs_referrals_free(dfs_referral_response_t * refrsp)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
smb_dfs_referrals_unclen(dfs_info_t * referrals,uint16_t refno)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