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 2013 Nexenta Systems, Inc. All rights reserved.
26 */
27
28 #include <smbsrv/smb_kproto.h>
29 #include <smbsrv/smb_dfs.h>
30 #include <smbsrv/smb_door.h>
31 #include <smbsrv/winioctl.h>
32
33 /*
34 * Get Referral response header flags
35 * For exact meaning refer to MS-DFSC spec.
36 *
37 * R: ReferralServers
38 * S: StorageServers
39 * T: TargetFailback
40 */
41 #define DFS_HDRFLG_R 0x00000001
42 #define DFS_HDRFLG_S 0x00000002
43 #define DFS_HDRFLG_T 0x00000004
44
45 /*
46 * Entry flags
47 */
48 #define DFS_ENTFLG_T 0x0004
49
50 /*
51 * Referral entry types/versions
67 #define DFS_REFV1_ENTSZ 8
68 #define DFS_REFV2_ENTSZ 22
69 #define DFS_REFV3_ENTSZ 34
70 #define DFS_REFV4_ENTSZ 34
71
72 static dfs_reftype_t smb_dfs_get_reftype(const char *);
73 static void smb_dfs_encode_hdr(mbuf_chain_t *, dfs_info_t *);
74 static uint32_t smb_dfs_encode_refv1(smb_request_t *, mbuf_chain_t *,
75 dfs_info_t *);
76 static uint32_t smb_dfs_encode_refv2(smb_request_t *, mbuf_chain_t *,
77 dfs_info_t *);
78 static uint32_t smb_dfs_encode_refv3x(smb_request_t *, mbuf_chain_t *,
79 dfs_info_t *, uint16_t);
80 static void smb_dfs_encode_targets(mbuf_chain_t *, dfs_info_t *);
81 static uint32_t smb_dfs_referrals_get(smb_request_t *, char *, dfs_reftype_t,
82 dfs_referral_response_t *);
83 static void smb_dfs_referrals_free(dfs_referral_response_t *);
84 static uint16_t smb_dfs_referrals_unclen(dfs_info_t *, uint16_t);
85
86 /*
87 * Note: SMB1 callers in smb_trans2_dfs.c
88 * smb_com_trans2_report_dfs_inconsistency
89 * smb_com_trans2_get_dfs_referral
90 */
91
92 /*
93 * See [MS-DFSC] for details about this command
94 */
95 uint32_t
96 smb_dfs_get_referrals(smb_request_t *sr, smb_fsctl_t *fsctl)
97 {
98 dfs_info_t *referrals;
99 dfs_referral_response_t refrsp;
100 dfs_reftype_t reftype;
101 char *path;
102 uint16_t maxver;
103 uint32_t status;
104 int rc;
105
106 /*
107 * The caller checks this, because the error reporting method
108 * varies across SMB versions.
109 */
110 ASSERT(STYPE_ISIPC(sr->tid_tree->t_res_type));
111
112 /*
113 * XXX Instead of decoding the referral request and encoding
114 * the response here (in-kernel) we could pass the given
115 * request buffer in our door call, and let that return the
116 * response buffer ready to stuff into out_mbc. That would
117 * allow all this decoding/encoding to happen at user-level.
118 * (and most of this file would go away. :-)
119 */
120 switch (fsctl->CtlCode) {
121 case FSCTL_DFS_GET_REFERRALS:
122 /*
123 * Input data is (w) MaxReferralLevel, (U) path
124 */
125 rc = smb_mbc_decodef(fsctl->in_mbc, "%wu",
126 sr, &maxver, &path);
127 if (rc != 0)
128 return (NT_STATUS_INVALID_PARAMETER);
129 break;
130
131 case FSCTL_DFS_GET_REFERRALS_EX: /* XXX - todo */
132 default:
133 return (NT_STATUS_NOT_SUPPORTED);
134 }
135
136 reftype = smb_dfs_get_reftype((const char *)path);
137 switch (reftype) {
138 case DFS_REFERRAL_INVALID:
139 /* Need to check the error for this case */
140 return (NT_STATUS_INVALID_PARAMETER);
141
142 case DFS_REFERRAL_DOMAIN:
143 case DFS_REFERRAL_DC:
144 /* MS-DFSC: this error is returned by non-DC root */
145 return (NT_STATUS_INVALID_PARAMETER);
146
147 case DFS_REFERRAL_SYSVOL:
148 /* MS-DFSC: this error is returned by non-DC root */
149 return (NT_STATUS_NO_SUCH_DEVICE);
150
151 default:
152 break;
153 }
154
155 status = smb_dfs_referrals_get(sr, path, reftype, &refrsp);
374 DFS_REFERRAL_V2, DFS_REFV2_ENTSZ, server_type, flags,
375 proximity, referrals->i_timeout, path_offs, altpath_offs,
376 netpath_offs);
377
378 total_targetsz += targetsz;
379 }
380
381 smb_dfs_encode_targets(mbc, referrals);
382
383 return (NT_STATUS_SUCCESS);
384 }
385
386 /*
387 * Prepare a response with V3/V4 referral format.
388 *
389 * For more details, see comments for smb_dfs_encode_refv2() or see
390 * MS-DFSC specification.
391 */
392 static uint32_t
393 smb_dfs_encode_refv3x(smb_request_t *sr, mbuf_chain_t *mbc,
394 dfs_info_t *referrals,
395 uint16_t ver)
396 {
397 _NOTE(ARGUNUSED(sr))
398 uint16_t entsize, rep_bufsize, hdrsize;
399 uint16_t server_type;
400 uint16_t flags = 0;
401 uint16_t path_offs, altpath_offs, netpath_offs;
402 uint16_t targetsz, total_targetsz = 0;
403 uint16_t dfs_pathsz;
404 uint16_t r;
405
406 hdrsize = (ver == DFS_REFERRAL_V3) ? DFS_REFV3_ENTSZ : DFS_REFV4_ENTSZ;
407 rep_bufsize = MBC_MAXBYTES(mbc);
408 dfs_pathsz = smb_wcequiv_strlen(referrals->i_uncpath) + 2;
409 entsize = hdrsize + dfs_pathsz + dfs_pathsz +
410 smb_dfs_referrals_unclen(referrals, 0);
411
412 if (entsize > rep_bufsize) {
413 /* need room for at least one referral */
414 return (NT_STATUS_BUFFER_OVERFLOW);
415 }
487 * Get referral information for the specified path from user space
488 * using a door call.
489 */
490 static uint32_t
491 smb_dfs_referrals_get(smb_request_t *sr, char *dfs_path, dfs_reftype_t reftype,
492 dfs_referral_response_t *refrsp)
493 {
494 dfs_referral_query_t req;
495 int rc;
496
497 req.rq_type = reftype;
498 req.rq_path = dfs_path;
499
500 bzero(refrsp, sizeof (dfs_referral_response_t));
501 refrsp->rp_status = NT_STATUS_NOT_FOUND;
502
503 rc = smb_kdoor_upcall(sr->sr_server, SMB_DR_DFS_GET_REFERRALS,
504 &req, dfs_referral_query_xdr, refrsp, dfs_referral_response_xdr);
505
506 if (rc != 0 || refrsp->rp_status != ERROR_SUCCESS) {
507 return (NT_STATUS_NO_SUCH_DEVICE);
508 }
509
510 (void) strsubst(refrsp->rp_referrals.i_uncpath, '/', '\\');
511 return (NT_STATUS_SUCCESS);
512 }
513
514 static void
515 smb_dfs_referrals_free(dfs_referral_response_t *refrsp)
516 {
517 xdr_free(dfs_referral_response_xdr, (char *)refrsp);
518 }
519
520 /*
521 * Returns the Unicode string length for the target UNC of
522 * the specified entry by 'refno'
523 *
524 * Note that the UNC path should be encoded with ONE leading
525 * slash not two as is common to user-visible UNC paths.
526 */
527 static uint16_t
|
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 */
27
28 #include <smbsrv/smb_kproto.h>
29 #include <smbsrv/smb_dfs.h>
30 #include <smbsrv/smb_door.h>
31 #include <smb/winioctl.h>
32
33 /*
34 * Get Referral response header flags
35 * For exact meaning refer to MS-DFSC spec.
36 *
37 * R: ReferralServers
38 * S: StorageServers
39 * T: TargetFailback
40 */
41 #define DFS_HDRFLG_R 0x00000001
42 #define DFS_HDRFLG_S 0x00000002
43 #define DFS_HDRFLG_T 0x00000004
44
45 /*
46 * Entry flags
47 */
48 #define DFS_ENTFLG_T 0x0004
49
50 /*
51 * Referral entry types/versions
67 #define DFS_REFV1_ENTSZ 8
68 #define DFS_REFV2_ENTSZ 22
69 #define DFS_REFV3_ENTSZ 34
70 #define DFS_REFV4_ENTSZ 34
71
72 static dfs_reftype_t smb_dfs_get_reftype(const char *);
73 static void smb_dfs_encode_hdr(mbuf_chain_t *, dfs_info_t *);
74 static uint32_t smb_dfs_encode_refv1(smb_request_t *, mbuf_chain_t *,
75 dfs_info_t *);
76 static uint32_t smb_dfs_encode_refv2(smb_request_t *, mbuf_chain_t *,
77 dfs_info_t *);
78 static uint32_t smb_dfs_encode_refv3x(smb_request_t *, mbuf_chain_t *,
79 dfs_info_t *, uint16_t);
80 static void smb_dfs_encode_targets(mbuf_chain_t *, dfs_info_t *);
81 static uint32_t smb_dfs_referrals_get(smb_request_t *, char *, dfs_reftype_t,
82 dfs_referral_response_t *);
83 static void smb_dfs_referrals_free(dfs_referral_response_t *);
84 static uint16_t smb_dfs_referrals_unclen(dfs_info_t *, uint16_t);
85
86 /*
87 * Handle device type FILE_DEVICE_DFS
88 * for smb2_ioctl
89 */
90 uint32_t
91 smb_dfs_fsctl(smb_request_t *sr, smb_fsctl_t *fsctl)
92 {
93 uint32_t status;
94
95 if (!STYPE_ISIPC(sr->tid_tree->t_res_type))
96 return (NT_STATUS_INVALID_DEVICE_REQUEST);
97
98 switch (fsctl->CtlCode) {
99 case FSCTL_DFS_GET_REFERRALS:
100 status = smb_dfs_get_referrals(sr, fsctl);
101 break;
102 case FSCTL_DFS_GET_REFERRALS_EX: /* XXX - todo */
103 default:
104 status = NT_STATUS_NOT_SUPPORTED;
105 }
106
107 return (status);
108 }
109
110 /*
111 * Note: SMB1 callers in smb_trans2_dfs.c
112 * smb_com_trans2_report_dfs_inconsistency
113 * smb_com_trans2_get_dfs_referral
114 */
115
116 /*
117 * See [MS-DFSC] for details about this command
118 * Handles FSCTL_DFS_GET_REFERRALS (only)
119 */
120 uint32_t
121 smb_dfs_get_referrals(smb_request_t *sr, smb_fsctl_t *fsctl)
122 {
123 dfs_info_t *referrals;
124 dfs_referral_response_t refrsp;
125 dfs_reftype_t reftype;
126 char *path;
127 uint16_t maxver;
128 uint32_t status;
129 int rc;
130
131 /*
132 * The caller checks this, because the error reporting method
133 * varies across SMB versions.
134 */
135 ASSERT(STYPE_ISIPC(sr->tid_tree->t_res_type));
136
137 /*
138 * XXX Instead of decoding the referral request and encoding
139 * the response here (in-kernel) we could pass the given
140 * request buffer in our door call, and let that return the
141 * response buffer ready to stuff into out_mbc. That would
142 * allow all this decoding/encoding to happen at user-level.
143 * (and most of this file would go away. :-)
144 */
145
146 /*
147 * Input data is (w) MaxReferralLevel, (U) path
148 */
149 rc = smb_mbc_decodef(fsctl->in_mbc, "%wu",
150 sr, &maxver, &path);
151 if (rc != 0)
152 return (NT_STATUS_INVALID_PARAMETER);
153
154 reftype = smb_dfs_get_reftype((const char *)path);
155 switch (reftype) {
156 case DFS_REFERRAL_INVALID:
157 /* Need to check the error for this case */
158 return (NT_STATUS_INVALID_PARAMETER);
159
160 case DFS_REFERRAL_DOMAIN:
161 case DFS_REFERRAL_DC:
162 /* MS-DFSC: this error is returned by non-DC root */
163 return (NT_STATUS_INVALID_PARAMETER);
164
165 case DFS_REFERRAL_SYSVOL:
166 /* MS-DFSC: this error is returned by non-DC root */
167 return (NT_STATUS_NO_SUCH_DEVICE);
168
169 default:
170 break;
171 }
172
173 status = smb_dfs_referrals_get(sr, path, reftype, &refrsp);
392 DFS_REFERRAL_V2, DFS_REFV2_ENTSZ, server_type, flags,
393 proximity, referrals->i_timeout, path_offs, altpath_offs,
394 netpath_offs);
395
396 total_targetsz += targetsz;
397 }
398
399 smb_dfs_encode_targets(mbc, referrals);
400
401 return (NT_STATUS_SUCCESS);
402 }
403
404 /*
405 * Prepare a response with V3/V4 referral format.
406 *
407 * For more details, see comments for smb_dfs_encode_refv2() or see
408 * MS-DFSC specification.
409 */
410 static uint32_t
411 smb_dfs_encode_refv3x(smb_request_t *sr, mbuf_chain_t *mbc,
412 dfs_info_t *referrals, uint16_t ver)
413 {
414 _NOTE(ARGUNUSED(sr))
415 uint16_t entsize, rep_bufsize, hdrsize;
416 uint16_t server_type;
417 uint16_t flags = 0;
418 uint16_t path_offs, altpath_offs, netpath_offs;
419 uint16_t targetsz, total_targetsz = 0;
420 uint16_t dfs_pathsz;
421 uint16_t r;
422
423 hdrsize = (ver == DFS_REFERRAL_V3) ? DFS_REFV3_ENTSZ : DFS_REFV4_ENTSZ;
424 rep_bufsize = MBC_MAXBYTES(mbc);
425 dfs_pathsz = smb_wcequiv_strlen(referrals->i_uncpath) + 2;
426 entsize = hdrsize + dfs_pathsz + dfs_pathsz +
427 smb_dfs_referrals_unclen(referrals, 0);
428
429 if (entsize > rep_bufsize) {
430 /* need room for at least one referral */
431 return (NT_STATUS_BUFFER_OVERFLOW);
432 }
504 * Get referral information for the specified path from user space
505 * using a door call.
506 */
507 static uint32_t
508 smb_dfs_referrals_get(smb_request_t *sr, char *dfs_path, dfs_reftype_t reftype,
509 dfs_referral_response_t *refrsp)
510 {
511 dfs_referral_query_t req;
512 int rc;
513
514 req.rq_type = reftype;
515 req.rq_path = dfs_path;
516
517 bzero(refrsp, sizeof (dfs_referral_response_t));
518 refrsp->rp_status = NT_STATUS_NOT_FOUND;
519
520 rc = smb_kdoor_upcall(sr->sr_server, SMB_DR_DFS_GET_REFERRALS,
521 &req, dfs_referral_query_xdr, refrsp, dfs_referral_response_xdr);
522
523 if (rc != 0 || refrsp->rp_status != ERROR_SUCCESS) {
524 return (NT_STATUS_FS_DRIVER_REQUIRED);
525 }
526
527 (void) strsubst(refrsp->rp_referrals.i_uncpath, '/', '\\');
528 return (NT_STATUS_SUCCESS);
529 }
530
531 static void
532 smb_dfs_referrals_free(dfs_referral_response_t *refrsp)
533 {
534 xdr_free(dfs_referral_response_xdr, (char *)refrsp);
535 }
536
537 /*
538 * Returns the Unicode string length for the target UNC of
539 * the specified entry by 'refno'
540 *
541 * Note that the UNC path should be encoded with ONE leading
542 * slash not two as is common to user-visible UNC paths.
543 */
544 static uint16_t
|