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