xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_dfs.c (revision a90cf9f29973990687fa61de9f1f6ea22e924e40)
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
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
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
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
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
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
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
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
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
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
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