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