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