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