1*a90cf9f2SGordon Ross /*
2*a90cf9f2SGordon Ross * CDDL HEADER START
3*a90cf9f2SGordon Ross *
4*a90cf9f2SGordon Ross * The contents of this file are subject to the terms of the
5*a90cf9f2SGordon Ross * Common Development and Distribution License (the "License").
6*a90cf9f2SGordon Ross * You may not use this file except in compliance with the License.
7*a90cf9f2SGordon Ross *
8*a90cf9f2SGordon Ross * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*a90cf9f2SGordon Ross * or http://www.opensolaris.org/os/licensing.
10*a90cf9f2SGordon Ross * See the License for the specific language governing permissions
11*a90cf9f2SGordon Ross * and limitations under the License.
12*a90cf9f2SGordon Ross *
13*a90cf9f2SGordon Ross * When distributing Covered Code, include this CDDL HEADER in each
14*a90cf9f2SGordon Ross * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*a90cf9f2SGordon Ross * If applicable, add the following below this CDDL HEADER, with the
16*a90cf9f2SGordon Ross * fields enclosed by brackets "[]" replaced with your own identifying
17*a90cf9f2SGordon Ross * information: Portions Copyright [yyyy] [name of copyright owner]
18*a90cf9f2SGordon Ross *
19*a90cf9f2SGordon Ross * CDDL HEADER END
20*a90cf9f2SGordon Ross */
21*a90cf9f2SGordon Ross /*
22*a90cf9f2SGordon Ross * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23*a90cf9f2SGordon Ross * Use is subject to license terms.
24*a90cf9f2SGordon Ross *
25*a90cf9f2SGordon Ross * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
26*a90cf9f2SGordon Ross */
27*a90cf9f2SGordon Ross
28*a90cf9f2SGordon Ross #include <smbsrv/smb_kproto.h>
29*a90cf9f2SGordon Ross #include <smbsrv/smb_dfs.h>
30*a90cf9f2SGordon Ross #include <smbsrv/smb_door.h>
31*a90cf9f2SGordon Ross #include <smbsrv/winioctl.h>
32*a90cf9f2SGordon Ross
33*a90cf9f2SGordon Ross /*
34*a90cf9f2SGordon Ross * Get Referral response header flags
35*a90cf9f2SGordon Ross * For exact meaning refer to MS-DFSC spec.
36*a90cf9f2SGordon Ross *
37*a90cf9f2SGordon Ross * R: ReferralServers
38*a90cf9f2SGordon Ross * S: StorageServers
39*a90cf9f2SGordon Ross * T: TargetFailback
40*a90cf9f2SGordon Ross */
41*a90cf9f2SGordon Ross #define DFS_HDRFLG_R 0x00000001
42*a90cf9f2SGordon Ross #define DFS_HDRFLG_S 0x00000002
43*a90cf9f2SGordon Ross #define DFS_HDRFLG_T 0x00000004
44*a90cf9f2SGordon Ross
45*a90cf9f2SGordon Ross /*
46*a90cf9f2SGordon Ross * Entry flags
47*a90cf9f2SGordon Ross */
48*a90cf9f2SGordon Ross #define DFS_ENTFLG_T 0x0004
49*a90cf9f2SGordon Ross
50*a90cf9f2SGordon Ross /*
51*a90cf9f2SGordon Ross * Referral entry types/versions
52*a90cf9f2SGordon Ross */
53*a90cf9f2SGordon Ross #define DFS_REFERRAL_V1 0x0001
54*a90cf9f2SGordon Ross #define DFS_REFERRAL_V2 0x0002
55*a90cf9f2SGordon Ross #define DFS_REFERRAL_V3 0x0003
56*a90cf9f2SGordon Ross #define DFS_REFERRAL_V4 0x0004
57*a90cf9f2SGordon Ross
58*a90cf9f2SGordon Ross /*
59*a90cf9f2SGordon Ross * Valid values for ServerType field in referral entries
60*a90cf9f2SGordon Ross */
61*a90cf9f2SGordon Ross #define DFS_SRVTYPE_NONROOT 0x0000
62*a90cf9f2SGordon Ross #define DFS_SRVTYPE_ROOT 0x0001
63*a90cf9f2SGordon Ross
64*a90cf9f2SGordon Ross /*
65*a90cf9f2SGordon Ross * Size of the fix part for each referral entry type
66*a90cf9f2SGordon Ross */
67*a90cf9f2SGordon Ross #define DFS_REFV1_ENTSZ 8
68*a90cf9f2SGordon Ross #define DFS_REFV2_ENTSZ 22
69*a90cf9f2SGordon Ross #define DFS_REFV3_ENTSZ 34
70*a90cf9f2SGordon Ross #define DFS_REFV4_ENTSZ 34
71*a90cf9f2SGordon Ross
72*a90cf9f2SGordon Ross static dfs_reftype_t smb_dfs_get_reftype(const char *);
73*a90cf9f2SGordon Ross static void smb_dfs_encode_hdr(mbuf_chain_t *, dfs_info_t *);
74*a90cf9f2SGordon Ross static uint32_t smb_dfs_encode_refv1(smb_request_t *, mbuf_chain_t *,
75*a90cf9f2SGordon Ross dfs_info_t *);
76*a90cf9f2SGordon Ross static uint32_t smb_dfs_encode_refv2(smb_request_t *, mbuf_chain_t *,
77*a90cf9f2SGordon Ross dfs_info_t *);
78*a90cf9f2SGordon Ross static uint32_t smb_dfs_encode_refv3x(smb_request_t *, mbuf_chain_t *,
79*a90cf9f2SGordon Ross dfs_info_t *, uint16_t);
80*a90cf9f2SGordon Ross static void smb_dfs_encode_targets(mbuf_chain_t *, dfs_info_t *);
81*a90cf9f2SGordon Ross static uint32_t smb_dfs_referrals_get(smb_request_t *, char *, dfs_reftype_t,
82*a90cf9f2SGordon Ross dfs_referral_response_t *);
83*a90cf9f2SGordon Ross static void smb_dfs_referrals_free(dfs_referral_response_t *);
84*a90cf9f2SGordon Ross static uint16_t smb_dfs_referrals_unclen(dfs_info_t *, uint16_t);
85*a90cf9f2SGordon Ross
86*a90cf9f2SGordon Ross /*
87*a90cf9f2SGordon Ross * Note: SMB1 callers in smb_trans2_dfs.c
88*a90cf9f2SGordon Ross * smb_com_trans2_report_dfs_inconsistency
89*a90cf9f2SGordon Ross * smb_com_trans2_get_dfs_referral
90*a90cf9f2SGordon Ross */
91*a90cf9f2SGordon Ross
92*a90cf9f2SGordon Ross /*
93*a90cf9f2SGordon Ross * See [MS-DFSC] for details about this command
94*a90cf9f2SGordon Ross */
95*a90cf9f2SGordon Ross uint32_t
smb_dfs_get_referrals(smb_request_t * sr,smb_fsctl_t * fsctl)96*a90cf9f2SGordon Ross smb_dfs_get_referrals(smb_request_t *sr, smb_fsctl_t *fsctl)
97*a90cf9f2SGordon Ross {
98*a90cf9f2SGordon Ross dfs_info_t *referrals;
99*a90cf9f2SGordon Ross dfs_referral_response_t refrsp;
100*a90cf9f2SGordon Ross dfs_reftype_t reftype;
101*a90cf9f2SGordon Ross char *path;
102*a90cf9f2SGordon Ross uint16_t maxver;
103*a90cf9f2SGordon Ross uint32_t status;
104*a90cf9f2SGordon Ross int rc;
105*a90cf9f2SGordon Ross
106*a90cf9f2SGordon Ross /*
107*a90cf9f2SGordon Ross * The caller checks this, because the error reporting method
108*a90cf9f2SGordon Ross * varies across SMB versions.
109*a90cf9f2SGordon Ross */
110*a90cf9f2SGordon Ross ASSERT(STYPE_ISIPC(sr->tid_tree->t_res_type));
111*a90cf9f2SGordon Ross
112*a90cf9f2SGordon Ross /*
113*a90cf9f2SGordon Ross * XXX Instead of decoding the referral request and encoding
114*a90cf9f2SGordon Ross * the response here (in-kernel) we could pass the given
115*a90cf9f2SGordon Ross * request buffer in our door call, and let that return the
116*a90cf9f2SGordon Ross * response buffer ready to stuff into out_mbc. That would
117*a90cf9f2SGordon Ross * allow all this decoding/encoding to happen at user-level.
118*a90cf9f2SGordon Ross * (and most of this file would go away. :-)
119*a90cf9f2SGordon Ross */
120*a90cf9f2SGordon Ross switch (fsctl->CtlCode) {
121*a90cf9f2SGordon Ross case FSCTL_DFS_GET_REFERRALS:
122*a90cf9f2SGordon Ross /*
123*a90cf9f2SGordon Ross * Input data is (w) MaxReferralLevel, (U) path
124*a90cf9f2SGordon Ross */
125*a90cf9f2SGordon Ross rc = smb_mbc_decodef(fsctl->in_mbc, "%wu",
126*a90cf9f2SGordon Ross sr, &maxver, &path);
127*a90cf9f2SGordon Ross if (rc != 0)
128*a90cf9f2SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
129*a90cf9f2SGordon Ross break;
130*a90cf9f2SGordon Ross
131*a90cf9f2SGordon Ross case FSCTL_DFS_GET_REFERRALS_EX: /* XXX - todo */
132*a90cf9f2SGordon Ross default:
133*a90cf9f2SGordon Ross return (NT_STATUS_NOT_SUPPORTED);
134*a90cf9f2SGordon Ross }
135*a90cf9f2SGordon Ross
136*a90cf9f2SGordon Ross reftype = smb_dfs_get_reftype((const char *)path);
137*a90cf9f2SGordon Ross switch (reftype) {
138*a90cf9f2SGordon Ross case DFS_REFERRAL_INVALID:
139*a90cf9f2SGordon Ross /* Need to check the error for this case */
140*a90cf9f2SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
141*a90cf9f2SGordon Ross
142*a90cf9f2SGordon Ross case DFS_REFERRAL_DOMAIN:
143*a90cf9f2SGordon Ross case DFS_REFERRAL_DC:
144*a90cf9f2SGordon Ross /* MS-DFSC: this error is returned by non-DC root */
145*a90cf9f2SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
146*a90cf9f2SGordon Ross
147*a90cf9f2SGordon Ross case DFS_REFERRAL_SYSVOL:
148*a90cf9f2SGordon Ross /* MS-DFSC: this error is returned by non-DC root */
149*a90cf9f2SGordon Ross return (NT_STATUS_NO_SUCH_DEVICE);
150*a90cf9f2SGordon Ross
151*a90cf9f2SGordon Ross default:
152*a90cf9f2SGordon Ross break;
153*a90cf9f2SGordon Ross }
154*a90cf9f2SGordon Ross
155*a90cf9f2SGordon Ross status = smb_dfs_referrals_get(sr, path, reftype, &refrsp);
156*a90cf9f2SGordon Ross if (status != NT_STATUS_SUCCESS)
157*a90cf9f2SGordon Ross return (status);
158*a90cf9f2SGordon Ross
159*a90cf9f2SGordon Ross referrals = &refrsp.rp_referrals;
160*a90cf9f2SGordon Ross smb_dfs_encode_hdr(fsctl->out_mbc, referrals);
161*a90cf9f2SGordon Ross
162*a90cf9f2SGordon Ross /*
163*a90cf9f2SGordon Ross * Server may respond with any referral version at or below
164*a90cf9f2SGordon Ross * the maximum specified in the request.
165*a90cf9f2SGordon Ross */
166*a90cf9f2SGordon Ross switch (maxver) {
167*a90cf9f2SGordon Ross case DFS_REFERRAL_V1:
168*a90cf9f2SGordon Ross status = smb_dfs_encode_refv1(sr, fsctl->out_mbc, referrals);
169*a90cf9f2SGordon Ross break;
170*a90cf9f2SGordon Ross
171*a90cf9f2SGordon Ross case DFS_REFERRAL_V2:
172*a90cf9f2SGordon Ross status = smb_dfs_encode_refv2(sr, fsctl->out_mbc, referrals);
173*a90cf9f2SGordon Ross break;
174*a90cf9f2SGordon Ross
175*a90cf9f2SGordon Ross case DFS_REFERRAL_V3:
176*a90cf9f2SGordon Ross status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals,
177*a90cf9f2SGordon Ross DFS_REFERRAL_V3);
178*a90cf9f2SGordon Ross break;
179*a90cf9f2SGordon Ross
180*a90cf9f2SGordon Ross case DFS_REFERRAL_V4:
181*a90cf9f2SGordon Ross default:
182*a90cf9f2SGordon Ross status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals,
183*a90cf9f2SGordon Ross DFS_REFERRAL_V4);
184*a90cf9f2SGordon Ross break;
185*a90cf9f2SGordon Ross }
186*a90cf9f2SGordon Ross
187*a90cf9f2SGordon Ross smb_dfs_referrals_free(&refrsp);
188*a90cf9f2SGordon Ross
189*a90cf9f2SGordon Ross return (status);
190*a90cf9f2SGordon Ross }
191*a90cf9f2SGordon Ross
192*a90cf9f2SGordon Ross /*
193*a90cf9f2SGordon Ross * [MS-DFSC]: REQ_GET_DFS_REFERRAL
194*a90cf9f2SGordon Ross *
195*a90cf9f2SGordon Ross * Determines the referral type based on the specified path:
196*a90cf9f2SGordon Ross *
197*a90cf9f2SGordon Ross * Domain referral:
198*a90cf9f2SGordon Ross * ""
199*a90cf9f2SGordon Ross *
200*a90cf9f2SGordon Ross * DC referral:
201*a90cf9f2SGordon Ross * \<domain>
202*a90cf9f2SGordon Ross *
203*a90cf9f2SGordon Ross * Sysvol referral:
204*a90cf9f2SGordon Ross * \<domain>\SYSVOL
205*a90cf9f2SGordon Ross * \<domain>\NETLOGON
206*a90cf9f2SGordon Ross *
207*a90cf9f2SGordon Ross * Root referral:
208*a90cf9f2SGordon Ross * \<domain>\<dfsname>
209*a90cf9f2SGordon Ross * \<server>\<dfsname>
210*a90cf9f2SGordon Ross *
211*a90cf9f2SGordon Ross * Link referral:
212*a90cf9f2SGordon Ross * \<domain>\<dfsname>\<linkpath>
213*a90cf9f2SGordon Ross * \<server>\<dfsname>\<linkpath>
214*a90cf9f2SGordon Ross */
215*a90cf9f2SGordon Ross static dfs_reftype_t
smb_dfs_get_reftype(const char * path)216*a90cf9f2SGordon Ross smb_dfs_get_reftype(const char *path)
217*a90cf9f2SGordon Ross {
218*a90cf9f2SGordon Ross smb_unc_t unc;
219*a90cf9f2SGordon Ross dfs_reftype_t reftype = 0;
220*a90cf9f2SGordon Ross
221*a90cf9f2SGordon Ross if (*path == '\0')
222*a90cf9f2SGordon Ross return (DFS_REFERRAL_DOMAIN);
223*a90cf9f2SGordon Ross
224*a90cf9f2SGordon Ross if (smb_unc_init(path, &unc) != 0)
225*a90cf9f2SGordon Ross return (DFS_REFERRAL_INVALID);
226*a90cf9f2SGordon Ross
227*a90cf9f2SGordon Ross if (unc.unc_path != NULL) {
228*a90cf9f2SGordon Ross reftype = DFS_REFERRAL_LINK;
229*a90cf9f2SGordon Ross } else if (unc.unc_share != NULL) {
230*a90cf9f2SGordon Ross if ((smb_strcasecmp(unc.unc_share, "SYSVOL", 0) == 0) ||
231*a90cf9f2SGordon Ross (smb_strcasecmp(unc.unc_share, "NETLOGON", 0) == 0)) {
232*a90cf9f2SGordon Ross reftype = DFS_REFERRAL_SYSVOL;
233*a90cf9f2SGordon Ross } else {
234*a90cf9f2SGordon Ross reftype = DFS_REFERRAL_ROOT;
235*a90cf9f2SGordon Ross }
236*a90cf9f2SGordon Ross } else if (unc.unc_server != NULL) {
237*a90cf9f2SGordon Ross reftype = DFS_REFERRAL_DC;
238*a90cf9f2SGordon Ross }
239*a90cf9f2SGordon Ross
240*a90cf9f2SGordon Ross smb_unc_free(&unc);
241*a90cf9f2SGordon Ross return (reftype);
242*a90cf9f2SGordon Ross }
243*a90cf9f2SGordon Ross
244*a90cf9f2SGordon Ross static void
smb_dfs_encode_hdr(mbuf_chain_t * mbc,dfs_info_t * referrals)245*a90cf9f2SGordon Ross smb_dfs_encode_hdr(mbuf_chain_t *mbc, dfs_info_t *referrals)
246*a90cf9f2SGordon Ross {
247*a90cf9f2SGordon Ross uint16_t path_consumed;
248*a90cf9f2SGordon Ross uint32_t flags;
249*a90cf9f2SGordon Ross
250*a90cf9f2SGordon Ross path_consumed = smb_wcequiv_strlen(referrals->i_uncpath);
251*a90cf9f2SGordon Ross flags = DFS_HDRFLG_S;
252*a90cf9f2SGordon Ross if (referrals->i_type == DFS_OBJECT_ROOT)
253*a90cf9f2SGordon Ross flags |= DFS_HDRFLG_R;
254*a90cf9f2SGordon Ross
255*a90cf9f2SGordon Ross /* Fill rep_param_mb in SMB1 caller. */
256*a90cf9f2SGordon Ross (void) smb_mbc_encodef(mbc, "wwl", path_consumed,
257*a90cf9f2SGordon Ross referrals->i_ntargets, flags);
258*a90cf9f2SGordon Ross }
259*a90cf9f2SGordon Ross
260*a90cf9f2SGordon Ross static uint32_t
smb_dfs_encode_refv1(smb_request_t * sr,mbuf_chain_t * mbc,dfs_info_t * referrals)261*a90cf9f2SGordon Ross smb_dfs_encode_refv1(smb_request_t *sr, mbuf_chain_t *mbc,
262*a90cf9f2SGordon Ross dfs_info_t *referrals)
263*a90cf9f2SGordon Ross {
264*a90cf9f2SGordon Ross _NOTE(ARGUNUSED(sr))
265*a90cf9f2SGordon Ross uint16_t entsize, rep_bufsize;
266*a90cf9f2SGordon Ross uint16_t server_type;
267*a90cf9f2SGordon Ross uint16_t flags = 0;
268*a90cf9f2SGordon Ross uint16_t r;
269*a90cf9f2SGordon Ross char *target;
270*a90cf9f2SGordon Ross
271*a90cf9f2SGordon Ross rep_bufsize = MBC_MAXBYTES(mbc);
272*a90cf9f2SGordon Ross
273*a90cf9f2SGordon Ross server_type = (referrals->i_type == DFS_OBJECT_ROOT) ?
274*a90cf9f2SGordon Ross DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT;
275*a90cf9f2SGordon Ross
276*a90cf9f2SGordon Ross target = kmem_alloc(MAXPATHLEN, KM_SLEEP);
277*a90cf9f2SGordon Ross
278*a90cf9f2SGordon Ross for (r = 0; r < referrals->i_ntargets; r++) {
279*a90cf9f2SGordon Ross (void) snprintf(target, MAXPATHLEN, "\\%s\\%s",
280*a90cf9f2SGordon Ross referrals->i_targets[r].t_server,
281*a90cf9f2SGordon Ross referrals->i_targets[r].t_share);
282*a90cf9f2SGordon Ross
283*a90cf9f2SGordon Ross entsize = DFS_REFV1_ENTSZ + smb_wcequiv_strlen(target) + 2;
284*a90cf9f2SGordon Ross if (entsize > rep_bufsize)
285*a90cf9f2SGordon Ross break;
286*a90cf9f2SGordon Ross
287*a90cf9f2SGordon Ross (void) smb_mbc_encodef(mbc, "wwwwU",
288*a90cf9f2SGordon Ross DFS_REFERRAL_V1, entsize, server_type, flags, target);
289*a90cf9f2SGordon Ross rep_bufsize -= entsize;
290*a90cf9f2SGordon Ross }
291*a90cf9f2SGordon Ross
292*a90cf9f2SGordon Ross kmem_free(target, MAXPATHLEN);
293*a90cf9f2SGordon Ross
294*a90cf9f2SGordon Ross /*
295*a90cf9f2SGordon Ross * Need room for at least one entry.
296*a90cf9f2SGordon Ross * Windows will silently drop targets that do not fit in
297*a90cf9f2SGordon Ross * the response buffer.
298*a90cf9f2SGordon Ross */
299*a90cf9f2SGordon Ross if (r == 0) {
300*a90cf9f2SGordon Ross return (NT_STATUS_BUFFER_OVERFLOW);
301*a90cf9f2SGordon Ross }
302*a90cf9f2SGordon Ross
303*a90cf9f2SGordon Ross return (NT_STATUS_SUCCESS);
304*a90cf9f2SGordon Ross }
305*a90cf9f2SGordon Ross
306*a90cf9f2SGordon Ross /*
307*a90cf9f2SGordon Ross * Prepare a response with V2 referral format.
308*a90cf9f2SGordon Ross *
309*a90cf9f2SGordon Ross * Here is the response packet format.
310*a90cf9f2SGordon Ross * All the strings come after all the fixed size entry headers.
311*a90cf9f2SGordon Ross * These headers contain offsets to the strings at the end. Note
312*a90cf9f2SGordon Ross * that the two "dfs_path" after the last entry is shared between
313*a90cf9f2SGordon Ross * all the entries.
314*a90cf9f2SGordon Ross *
315*a90cf9f2SGordon Ross * ent1-hdr
316*a90cf9f2SGordon Ross * ent2-hdr
317*a90cf9f2SGordon Ross * ...
318*a90cf9f2SGordon Ross * entN-hdr
319*a90cf9f2SGordon Ross * dfs_path
320*a90cf9f2SGordon Ross * dfs_path
321*a90cf9f2SGordon Ross * target1
322*a90cf9f2SGordon Ross * target2
323*a90cf9f2SGordon Ross * ...
324*a90cf9f2SGordon Ross * targetN
325*a90cf9f2SGordon Ross *
326*a90cf9f2SGordon Ross * MS-DFSC mentions that strings can come after each entry header or all after
327*a90cf9f2SGordon Ross * the last entry header. Windows responses are in the format above.
328*a90cf9f2SGordon Ross */
329*a90cf9f2SGordon Ross static uint32_t
smb_dfs_encode_refv2(smb_request_t * sr,mbuf_chain_t * mbc,dfs_info_t * referrals)330*a90cf9f2SGordon Ross smb_dfs_encode_refv2(smb_request_t *sr, mbuf_chain_t *mbc,
331*a90cf9f2SGordon Ross dfs_info_t *referrals)
332*a90cf9f2SGordon Ross {
333*a90cf9f2SGordon Ross _NOTE(ARGUNUSED(sr))
334*a90cf9f2SGordon Ross uint16_t entsize, rep_bufsize;
335*a90cf9f2SGordon Ross uint16_t server_type;
336*a90cf9f2SGordon Ross uint16_t flags = 0;
337*a90cf9f2SGordon Ross uint32_t proximity = 0;
338*a90cf9f2SGordon Ross uint16_t path_offs, altpath_offs, netpath_offs;
339*a90cf9f2SGordon Ross uint16_t targetsz, total_targetsz = 0;
340*a90cf9f2SGordon Ross uint16_t dfs_pathsz;
341*a90cf9f2SGordon Ross uint16_t r;
342*a90cf9f2SGordon Ross
343*a90cf9f2SGordon Ross rep_bufsize = MBC_MAXBYTES(mbc);
344*a90cf9f2SGordon Ross dfs_pathsz = smb_wcequiv_strlen(referrals->i_uncpath) + 2;
345*a90cf9f2SGordon Ross entsize = DFS_REFV2_ENTSZ + dfs_pathsz + dfs_pathsz +
346*a90cf9f2SGordon Ross smb_dfs_referrals_unclen(referrals, 0);
347*a90cf9f2SGordon Ross
348*a90cf9f2SGordon Ross if (entsize > rep_bufsize) {
349*a90cf9f2SGordon Ross /* need room for at least one referral */
350*a90cf9f2SGordon Ross return (NT_STATUS_BUFFER_OVERFLOW);
351*a90cf9f2SGordon Ross }
352*a90cf9f2SGordon Ross
353*a90cf9f2SGordon Ross server_type = (referrals->i_type == DFS_OBJECT_ROOT) ?
354*a90cf9f2SGordon Ross DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT;
355*a90cf9f2SGordon Ross
356*a90cf9f2SGordon Ross rep_bufsize -= entsize;
357*a90cf9f2SGordon Ross entsize = DFS_REFV2_ENTSZ;
358*a90cf9f2SGordon Ross
359*a90cf9f2SGordon Ross for (r = 0; r < referrals->i_ntargets; r++) {
360*a90cf9f2SGordon Ross path_offs = (referrals->i_ntargets - r) * DFS_REFV2_ENTSZ;
361*a90cf9f2SGordon Ross altpath_offs = path_offs + dfs_pathsz;
362*a90cf9f2SGordon Ross netpath_offs = altpath_offs + dfs_pathsz + total_targetsz;
363*a90cf9f2SGordon Ross targetsz = smb_dfs_referrals_unclen(referrals, r);
364*a90cf9f2SGordon Ross
365*a90cf9f2SGordon Ross if (r != 0) {
366*a90cf9f2SGordon Ross entsize = DFS_REFV2_ENTSZ + targetsz;
367*a90cf9f2SGordon Ross if (entsize > rep_bufsize)
368*a90cf9f2SGordon Ross /* silently drop targets that do not fit */
369*a90cf9f2SGordon Ross break;
370*a90cf9f2SGordon Ross rep_bufsize -= entsize;
371*a90cf9f2SGordon Ross }
372*a90cf9f2SGordon Ross
373*a90cf9f2SGordon Ross (void) smb_mbc_encodef(mbc, "wwwwllwww",
374*a90cf9f2SGordon Ross DFS_REFERRAL_V2, DFS_REFV2_ENTSZ, server_type, flags,
375*a90cf9f2SGordon Ross proximity, referrals->i_timeout, path_offs, altpath_offs,
376*a90cf9f2SGordon Ross netpath_offs);
377*a90cf9f2SGordon Ross
378*a90cf9f2SGordon Ross total_targetsz += targetsz;
379*a90cf9f2SGordon Ross }
380*a90cf9f2SGordon Ross
381*a90cf9f2SGordon Ross smb_dfs_encode_targets(mbc, referrals);
382*a90cf9f2SGordon Ross
383*a90cf9f2SGordon Ross return (NT_STATUS_SUCCESS);
384*a90cf9f2SGordon Ross }
385*a90cf9f2SGordon Ross
386*a90cf9f2SGordon Ross /*
387*a90cf9f2SGordon Ross * Prepare a response with V3/V4 referral format.
388*a90cf9f2SGordon Ross *
389*a90cf9f2SGordon Ross * For more details, see comments for smb_dfs_encode_refv2() or see
390*a90cf9f2SGordon Ross * MS-DFSC specification.
391*a90cf9f2SGordon Ross */
392*a90cf9f2SGordon Ross static uint32_t
smb_dfs_encode_refv3x(smb_request_t * sr,mbuf_chain_t * mbc,dfs_info_t * referrals,uint16_t ver)393*a90cf9f2SGordon Ross smb_dfs_encode_refv3x(smb_request_t *sr, mbuf_chain_t *mbc,
394*a90cf9f2SGordon Ross dfs_info_t *referrals,
395*a90cf9f2SGordon Ross uint16_t ver)
396*a90cf9f2SGordon Ross {
397*a90cf9f2SGordon Ross _NOTE(ARGUNUSED(sr))
398*a90cf9f2SGordon Ross uint16_t entsize, rep_bufsize, hdrsize;
399*a90cf9f2SGordon Ross uint16_t server_type;
400*a90cf9f2SGordon Ross uint16_t flags = 0;
401*a90cf9f2SGordon Ross uint16_t path_offs, altpath_offs, netpath_offs;
402*a90cf9f2SGordon Ross uint16_t targetsz, total_targetsz = 0;
403*a90cf9f2SGordon Ross uint16_t dfs_pathsz;
404*a90cf9f2SGordon Ross uint16_t r;
405*a90cf9f2SGordon Ross
406*a90cf9f2SGordon Ross hdrsize = (ver == DFS_REFERRAL_V3) ? DFS_REFV3_ENTSZ : DFS_REFV4_ENTSZ;
407*a90cf9f2SGordon Ross rep_bufsize = MBC_MAXBYTES(mbc);
408*a90cf9f2SGordon Ross dfs_pathsz = smb_wcequiv_strlen(referrals->i_uncpath) + 2;
409*a90cf9f2SGordon Ross entsize = hdrsize + dfs_pathsz + dfs_pathsz +
410*a90cf9f2SGordon Ross smb_dfs_referrals_unclen(referrals, 0);
411*a90cf9f2SGordon Ross
412*a90cf9f2SGordon Ross if (entsize > rep_bufsize) {
413*a90cf9f2SGordon Ross /* need room for at least one referral */
414*a90cf9f2SGordon Ross return (NT_STATUS_BUFFER_OVERFLOW);
415*a90cf9f2SGordon Ross }
416*a90cf9f2SGordon Ross
417*a90cf9f2SGordon Ross server_type = (referrals->i_type == DFS_OBJECT_ROOT) ?
418*a90cf9f2SGordon Ross DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT;
419*a90cf9f2SGordon Ross
420*a90cf9f2SGordon Ross rep_bufsize -= entsize;
421*a90cf9f2SGordon Ross
422*a90cf9f2SGordon Ross for (r = 0; r < referrals->i_ntargets; r++) {
423*a90cf9f2SGordon Ross path_offs = (referrals->i_ntargets - r) * hdrsize;
424*a90cf9f2SGordon Ross altpath_offs = path_offs + dfs_pathsz;
425*a90cf9f2SGordon Ross netpath_offs = altpath_offs + dfs_pathsz + total_targetsz;
426*a90cf9f2SGordon Ross targetsz = smb_dfs_referrals_unclen(referrals, r);
427*a90cf9f2SGordon Ross
428*a90cf9f2SGordon Ross if (r != 0) {
429*a90cf9f2SGordon Ross entsize = hdrsize + targetsz;
430*a90cf9f2SGordon Ross if (entsize > rep_bufsize)
431*a90cf9f2SGordon Ross /* silently drop targets that do not fit */
432*a90cf9f2SGordon Ross break;
433*a90cf9f2SGordon Ross rep_bufsize -= entsize;
434*a90cf9f2SGordon Ross flags = 0;
435*a90cf9f2SGordon Ross } else if (ver == DFS_REFERRAL_V4) {
436*a90cf9f2SGordon Ross flags = DFS_ENTFLG_T;
437*a90cf9f2SGordon Ross }
438*a90cf9f2SGordon Ross
439*a90cf9f2SGordon Ross (void) smb_mbc_encodef(mbc, "wwwwlwww16.",
440*a90cf9f2SGordon Ross ver, hdrsize, server_type, flags,
441*a90cf9f2SGordon Ross referrals->i_timeout, path_offs, altpath_offs,
442*a90cf9f2SGordon Ross netpath_offs);
443*a90cf9f2SGordon Ross
444*a90cf9f2SGordon Ross total_targetsz += targetsz;
445*a90cf9f2SGordon Ross }
446*a90cf9f2SGordon Ross
447*a90cf9f2SGordon Ross smb_dfs_encode_targets(mbc, referrals);
448*a90cf9f2SGordon Ross
449*a90cf9f2SGordon Ross return (NT_STATUS_SUCCESS);
450*a90cf9f2SGordon Ross }
451*a90cf9f2SGordon Ross
452*a90cf9f2SGordon Ross /*
453*a90cf9f2SGordon Ross * Encodes DFS path, and target strings which come after fixed header
454*a90cf9f2SGordon Ross * entries.
455*a90cf9f2SGordon Ross *
456*a90cf9f2SGordon Ross * Windows 2000 and earlier set the DFSAlternatePathOffset to point to
457*a90cf9f2SGordon Ross * an 8.3 string representation of the string pointed to by
458*a90cf9f2SGordon Ross * DFSPathOffset if it is not a legal 8.3 string. Otherwise, if
459*a90cf9f2SGordon Ross * DFSPathOffset points to a legal 8.3 string, DFSAlternatePathOffset
460*a90cf9f2SGordon Ross * points to a separate copy of the same string. Windows Server 2003,
461*a90cf9f2SGordon Ross * Windows Server 2008 and Windows Server 2008 R2 set the
462*a90cf9f2SGordon Ross * DFSPathOffset and DFSAlternatePathOffset fields to point to separate
463*a90cf9f2SGordon Ross * copies of the identical string.
464*a90cf9f2SGordon Ross *
465*a90cf9f2SGordon Ross * Following Windows 2003 and later here.
466*a90cf9f2SGordon Ross */
467*a90cf9f2SGordon Ross static void
smb_dfs_encode_targets(mbuf_chain_t * mbc,dfs_info_t * referrals)468*a90cf9f2SGordon Ross smb_dfs_encode_targets(mbuf_chain_t *mbc, dfs_info_t *referrals)
469*a90cf9f2SGordon Ross {
470*a90cf9f2SGordon Ross char *target;
471*a90cf9f2SGordon Ross int r;
472*a90cf9f2SGordon Ross
473*a90cf9f2SGordon Ross (void) smb_mbc_encodef(mbc, "UU", referrals->i_uncpath,
474*a90cf9f2SGordon Ross referrals->i_uncpath);
475*a90cf9f2SGordon Ross
476*a90cf9f2SGordon Ross target = kmem_alloc(MAXPATHLEN, KM_SLEEP);
477*a90cf9f2SGordon Ross for (r = 0; r < referrals->i_ntargets; r++) {
478*a90cf9f2SGordon Ross (void) snprintf(target, MAXPATHLEN, "\\%s\\%s",
479*a90cf9f2SGordon Ross referrals->i_targets[r].t_server,
480*a90cf9f2SGordon Ross referrals->i_targets[r].t_share);
481*a90cf9f2SGordon Ross (void) smb_mbc_encodef(mbc, "U", target);
482*a90cf9f2SGordon Ross }
483*a90cf9f2SGordon Ross kmem_free(target, MAXPATHLEN);
484*a90cf9f2SGordon Ross }
485*a90cf9f2SGordon Ross
486*a90cf9f2SGordon Ross /*
487*a90cf9f2SGordon Ross * Get referral information for the specified path from user space
488*a90cf9f2SGordon Ross * using a door call.
489*a90cf9f2SGordon Ross */
490*a90cf9f2SGordon Ross static uint32_t
smb_dfs_referrals_get(smb_request_t * sr,char * dfs_path,dfs_reftype_t reftype,dfs_referral_response_t * refrsp)491*a90cf9f2SGordon Ross smb_dfs_referrals_get(smb_request_t *sr, char *dfs_path, dfs_reftype_t reftype,
492*a90cf9f2SGordon Ross dfs_referral_response_t *refrsp)
493*a90cf9f2SGordon Ross {
494*a90cf9f2SGordon Ross dfs_referral_query_t req;
495*a90cf9f2SGordon Ross int rc;
496*a90cf9f2SGordon Ross
497*a90cf9f2SGordon Ross req.rq_type = reftype;
498*a90cf9f2SGordon Ross req.rq_path = dfs_path;
499*a90cf9f2SGordon Ross
500*a90cf9f2SGordon Ross bzero(refrsp, sizeof (dfs_referral_response_t));
501*a90cf9f2SGordon Ross refrsp->rp_status = NT_STATUS_NOT_FOUND;
502*a90cf9f2SGordon Ross
503*a90cf9f2SGordon Ross rc = smb_kdoor_upcall(sr->sr_server, SMB_DR_DFS_GET_REFERRALS,
504*a90cf9f2SGordon Ross &req, dfs_referral_query_xdr, refrsp, dfs_referral_response_xdr);
505*a90cf9f2SGordon Ross
506*a90cf9f2SGordon Ross if (rc != 0 || refrsp->rp_status != ERROR_SUCCESS) {
507*a90cf9f2SGordon Ross return (NT_STATUS_NO_SUCH_DEVICE);
508*a90cf9f2SGordon Ross }
509*a90cf9f2SGordon Ross
510*a90cf9f2SGordon Ross (void) strsubst(refrsp->rp_referrals.i_uncpath, '/', '\\');
511*a90cf9f2SGordon Ross return (NT_STATUS_SUCCESS);
512*a90cf9f2SGordon Ross }
513*a90cf9f2SGordon Ross
514*a90cf9f2SGordon Ross static void
smb_dfs_referrals_free(dfs_referral_response_t * refrsp)515*a90cf9f2SGordon Ross smb_dfs_referrals_free(dfs_referral_response_t *refrsp)
516*a90cf9f2SGordon Ross {
517*a90cf9f2SGordon Ross xdr_free(dfs_referral_response_xdr, (char *)refrsp);
518*a90cf9f2SGordon Ross }
519*a90cf9f2SGordon Ross
520*a90cf9f2SGordon Ross /*
521*a90cf9f2SGordon Ross * Returns the Unicode string length for the target UNC of
522*a90cf9f2SGordon Ross * the specified entry by 'refno'
523*a90cf9f2SGordon Ross *
524*a90cf9f2SGordon Ross * Note that the UNC path should be encoded with ONE leading
525*a90cf9f2SGordon Ross * slash not two as is common to user-visible UNC paths.
526*a90cf9f2SGordon Ross */
527*a90cf9f2SGordon Ross static uint16_t
smb_dfs_referrals_unclen(dfs_info_t * referrals,uint16_t refno)528*a90cf9f2SGordon Ross smb_dfs_referrals_unclen(dfs_info_t *referrals, uint16_t refno)
529*a90cf9f2SGordon Ross {
530*a90cf9f2SGordon Ross uint16_t len;
531*a90cf9f2SGordon Ross
532*a90cf9f2SGordon Ross if (refno >= referrals->i_ntargets)
533*a90cf9f2SGordon Ross return (0);
534*a90cf9f2SGordon Ross
535*a90cf9f2SGordon Ross /* Encoded target UNC \server\share */
536*a90cf9f2SGordon Ross len = smb_wcequiv_strlen(referrals->i_targets[refno].t_server) +
537*a90cf9f2SGordon Ross smb_wcequiv_strlen(referrals->i_targets[refno].t_share) +
538*a90cf9f2SGordon Ross smb_wcequiv_strlen("\\\\") + 2; /* two '\' + NULL */
539*a90cf9f2SGordon Ross
540*a90cf9f2SGordon Ross return (len);
541*a90cf9f2SGordon Ross }
542