1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/errno.h> 27 #include <string.h> 28 #include <strings.h> 29 30 #include <smbsrv/libsmb.h> 31 #include <smbsrv/libmlrpc.h> 32 33 #define NDR_IS_LAST_FRAG(F) ((F) & NDR_PFC_LAST_FRAG) 34 #define NDR_DEFAULT_FRAGSZ 8192 35 36 static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_t *); 37 static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *); 38 static void ndr_clnt_remove_hdr(ndr_stream_t *, int *); 39 40 int 41 ndr_clnt_bind(ndr_client_t *clnt, const char *service_name, 42 ndr_binding_t **ret_binding_p) 43 { 44 ndr_service_t *msvc; 45 ndr_binding_t *mbind; 46 ndr_xa_t mxa; 47 ndr_bind_hdr_t *bhdr; 48 ndr_p_cont_elem_t *pce; 49 ndr_bind_ack_hdr_t *bahdr; 50 ndr_p_result_t *pre; 51 int rc; 52 53 bzero(&mxa, sizeof (mxa)); 54 55 msvc = ndr_svc_lookup_name(service_name); 56 if (msvc == NULL) 57 return (NDR_DRC_FAULT_API_SERVICE_INVALID); 58 59 mxa.binding_list = clnt->binding_list; 60 if ((mbind = ndr_svc_new_binding(&mxa)) == NULL) 61 return (NDR_DRC_FAULT_API_BIND_NO_SLOTS); 62 63 ndr_clnt_init_hdr(clnt, &mxa); 64 65 bhdr = &mxa.send_hdr.bind_hdr; 66 bhdr->common_hdr.ptype = NDR_PTYPE_BIND; 67 bhdr->common_hdr.frag_length = sizeof (*bhdr); 68 bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ; 69 bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ; 70 bhdr->assoc_group_id = 0; 71 bhdr->p_context_elem.n_context_elem = 1; 72 73 /* Assign presentation context id */ 74 pce = &bhdr->p_context_elem.p_cont_elem[0]; 75 pce->p_cont_id = clnt->next_p_cont_id++; 76 pce->n_transfer_syn = 1; 77 78 /* Set up UUIDs and versions from the service */ 79 pce->abstract_syntax.if_version = msvc->abstract_syntax_version; 80 rc = ndr_uuid_parse(msvc->abstract_syntax_uuid, 81 &pce->abstract_syntax.if_uuid); 82 if (rc != 0) 83 return (NDR_DRC_FAULT_API_SERVICE_INVALID); 84 85 pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version; 86 rc = ndr_uuid_parse(msvc->transfer_syntax_uuid, 87 &pce->transfer_syntaxes[0].if_uuid); 88 if (rc != 0) 89 return (NDR_DRC_FAULT_API_SERVICE_INVALID); 90 91 /* Format and exchange the PDU */ 92 93 if ((*clnt->xa_init)(clnt, &mxa) < 0) 94 return (NDR_DRC_FAULT_OUT_OF_MEMORY); 95 96 rc = ndr_encode_pdu_hdr(&mxa); 97 if (NDR_DRC_IS_FAULT(rc)) 98 goto fault_exit; 99 100 if ((*clnt->xa_exchange)(clnt, &mxa) < 0) { 101 rc = NDR_DRC_FAULT_SEND_FAILED; 102 goto fault_exit; 103 } 104 105 rc = ndr_decode_pdu_hdr(&mxa); 106 if (NDR_DRC_IS_FAULT(rc)) 107 goto fault_exit; 108 109 /* done with buffers */ 110 (*clnt->xa_destruct)(clnt, &mxa); 111 112 bahdr = &mxa.recv_hdr.bind_ack_hdr; 113 114 if (mxa.ptype != NDR_PTYPE_BIND_ACK) 115 return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 116 117 if (bahdr->p_result_list.n_results != 1) 118 return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 119 120 pre = &bahdr->p_result_list.p_results[0]; 121 122 if (pre->result != NDR_PCDR_ACCEPTANCE) 123 return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 124 125 mbind->p_cont_id = pce->p_cont_id; 126 mbind->which_side = NDR_BIND_SIDE_CLIENT; 127 mbind->clnt = clnt; 128 mbind->service = msvc; 129 mbind->instance_specific = 0; 130 131 *ret_binding_p = mbind; 132 return (NDR_DRC_OK); 133 134 fault_exit: 135 (*clnt->xa_destruct)(clnt, &mxa); 136 return (rc); 137 } 138 139 int 140 ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params) 141 { 142 ndr_client_t *clnt = mbind->clnt; 143 ndr_service_t *msvc = mbind->service; 144 ndr_xa_t mxa; 145 ndr_request_hdr_t *reqhdr; 146 ndr_common_header_t *rsphdr; 147 unsigned long recv_pdu_scan_offset; 148 int rc; 149 150 if (ndr_svc_find_stub(msvc, opnum) == NULL) 151 return (NDR_DRC_FAULT_API_OPNUM_INVALID); 152 153 bzero(&mxa, sizeof (mxa)); 154 mxa.ptype = NDR_PTYPE_REQUEST; 155 mxa.opnum = opnum; 156 mxa.binding = mbind; 157 158 ndr_clnt_init_hdr(clnt, &mxa); 159 160 reqhdr = &mxa.send_hdr.request_hdr; 161 reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST; 162 reqhdr->p_cont_id = mbind->p_cont_id; 163 reqhdr->opnum = opnum; 164 165 rc = (*clnt->xa_init)(clnt, &mxa); 166 if (NDR_DRC_IS_FAULT(rc)) 167 return (rc); 168 169 /* Reserve room for hdr */ 170 mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr); 171 172 rc = ndr_encode_call(&mxa, params); 173 if (!NDR_DRC_IS_OK(rc)) 174 goto fault_exit; 175 176 mxa.send_nds.pdu_scan_offset = 0; 177 178 /* 179 * Now we have the PDU size, we need to set up the 180 * frag_length and calculate the alloc_hint. 181 */ 182 mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size; 183 reqhdr->alloc_hint = mxa.send_nds.pdu_size - 184 sizeof (ndr_request_hdr_t); 185 186 rc = ndr_encode_pdu_hdr(&mxa); 187 if (NDR_DRC_IS_FAULT(rc)) 188 goto fault_exit; 189 190 rc = (*clnt->xa_exchange)(clnt, &mxa); 191 if (NDR_DRC_IS_FAULT(rc)) 192 goto fault_exit; 193 194 rc = ndr_decode_pdu_hdr(&mxa); 195 if (NDR_DRC_IS_FAULT(rc)) 196 goto fault_exit; 197 198 if (mxa.ptype != NDR_PTYPE_RESPONSE) { 199 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 200 goto fault_exit; 201 } 202 203 rsphdr = &mxa.recv_hdr.common_hdr; 204 205 if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) { 206 /* 207 * This is a multi-fragment response. 208 * Preserve the current scan offset while getting 209 * fragments so that we can continue afterward 210 * as if we had received the entire response as 211 * a single PDU. 212 */ 213 recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset; 214 215 if (ndr_clnt_get_frags(clnt, &mxa) < 0) { 216 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 217 goto fault_exit; 218 } 219 220 mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset; 221 } 222 223 rc = ndr_decode_return(&mxa, params); 224 if (NDR_DRC_IS_FAULT(rc)) 225 goto fault_exit; 226 227 (*clnt->xa_preserve)(clnt, &mxa); 228 (*clnt->xa_destruct)(clnt, &mxa); 229 return (NDR_DRC_OK); 230 231 fault_exit: 232 (*clnt->xa_destruct)(clnt, &mxa); 233 return (rc); 234 } 235 236 void 237 ndr_clnt_free_heap(ndr_client_t *clnt) 238 { 239 (*clnt->xa_release)(clnt); 240 } 241 242 static void 243 ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa) 244 { 245 ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; 246 247 hdr->rpc_vers = 5; 248 hdr->rpc_vers_minor = 0; 249 hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; 250 hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII; 251 #ifndef _BIG_ENDIAN 252 hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN; 253 #endif 254 /* hdr->frag_length */ 255 hdr->auth_length = 0; 256 hdr->call_id = clnt->next_call_id++; 257 } 258 259 /* 260 * ndr_clnt_remove_hdr 261 * 262 * Remove an RPC fragment header from the received data stream. 263 * 264 * Original RPC receive buffer: 265 * |- frag1 -| |-frag M(partial)-| 266 * +==================+=============+----+=================+ 267 * | SmbTransact Rsp1 | SmbTransact | | SmbReadX RspN | 268 * | (with RPC hdr) | Rsp2 | .. | (with RPC hdr) | 269 * +-----+------------+-------------+ +-----+-----------+ 270 * | hdr | data | data | .. | hdr | data | 271 * +=====+============+=============+----+=====+===========+ 272 * <------ 273 * ^ ^ ^ 274 * | | | 275 * base_offset hdr data 276 * 277 * |-------------------------------------|-----------------| 278 * offset len 279 * 280 * RPC receive buffer (after this call): 281 * +==================+=============+----+===========+ 282 * | SmbTransact Rsp1 | SmbTransact | | SmbReadX | 283 * | (with RPC hdr) | Rsp2 | .. | RspN | 284 * +-----+------------+-------------+ +-----------+ 285 * | hdr | data | data | .. | data | 286 * +=====+============+=============+----+===========+ 287 */ 288 static void 289 ndr_clnt_remove_hdr(ndr_stream_t *nds, int *nbytes) 290 { 291 char *hdr; 292 char *data; 293 294 hdr = (char *)nds->pdu_base_offset + nds->pdu_scan_offset; 295 data = hdr + NDR_RSP_HDR_SIZE; 296 *nbytes -= NDR_RSP_HDR_SIZE; 297 298 bcopy(data, hdr, *nbytes); 299 nds->pdu_size -= NDR_RSP_HDR_SIZE; 300 } 301 302 /* 303 * ndr_clnt_get_frags 304 * 305 * A DCE RPC message that is larger than a single fragment is transmitted 306 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for 307 * both Windows 2000 and 2003. 308 * 309 * Collect RPC fragments and append them to the receive stream buffer. 310 * Each received fragment has a header, which we need to remove as we 311 * build the full RPC PDU. 312 * 313 * The xa_read() calls will translate to SmbReadX requests. Note that 314 * there is no correspondence between SmbReadX buffering and DCE RPC 315 * fragment alignment. 316 * 317 * Return -1 on error. Otherwise, return the total data count of the 318 * complete RPC response upon success. 319 */ 320 static int 321 ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa) 322 { 323 ndr_stream_t *nds = &mxa->recv_nds; 324 ndr_common_header_t hdr; 325 int frag_rcvd; 326 int frag_size; 327 int last_frag; 328 int nbytes; 329 330 /* 331 * The scan offest will be used to locate the frag header. 332 */ 333 nds->pdu_scan_offset = nds->pdu_base_offset + nds->pdu_size; 334 335 do { 336 frag_rcvd = 0; 337 338 do { 339 if ((nbytes = (*clnt->xa_read)(clnt, mxa)) < 0) 340 return (-1); 341 342 if (frag_rcvd == 0) { 343 ndr_decode_frag_hdr(nds, &hdr); 344 345 last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags); 346 frag_size = hdr.frag_length - NDR_RSP_HDR_SIZE; 347 348 ndr_clnt_remove_hdr(nds, &nbytes); 349 nds->pdu_scan_offset += frag_size; 350 } 351 352 frag_rcvd += nbytes; 353 354 } while (frag_rcvd < frag_size); 355 } while (!last_frag); 356 357 return (0); 358 } 359