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 2009 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_DEFAULT_FRAGSZ 8192 34 #define NDR_MULTI_FRAGSZ (60 * 1024) 35 36 static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_t *); 37 static void ndr_clnt_remove_hdr(ndr_stream_t *); 38 static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *); 39 static int ndr_clnt_get_frag(ndr_client_t *, ndr_xa_t *, ndr_common_header_t *); 40 41 int 42 ndr_clnt_bind(ndr_client_t *clnt, const char *service_name, 43 ndr_binding_t **ret_binding_p) 44 { 45 ndr_service_t *msvc; 46 ndr_binding_t *mbind; 47 ndr_xa_t mxa; 48 ndr_bind_hdr_t *bhdr; 49 ndr_p_cont_elem_t *pce; 50 ndr_bind_ack_hdr_t *bahdr; 51 ndr_p_result_t *pre; 52 int rc; 53 54 bzero(&mxa, sizeof (mxa)); 55 56 msvc = ndr_svc_lookup_name(service_name); 57 if (msvc == NULL) 58 return (NDR_DRC_FAULT_API_SERVICE_INVALID); 59 60 mxa.binding_list = clnt->binding_list; 61 if ((mbind = ndr_svc_new_binding(&mxa)) == NULL) 62 return (NDR_DRC_FAULT_API_BIND_NO_SLOTS); 63 64 ndr_clnt_init_hdr(clnt, &mxa); 65 66 bhdr = &mxa.send_hdr.bind_hdr; 67 bhdr->common_hdr.ptype = NDR_PTYPE_BIND; 68 bhdr->common_hdr.frag_length = sizeof (*bhdr); 69 bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ; 70 bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ; 71 bhdr->assoc_group_id = 0; 72 bhdr->p_context_elem.n_context_elem = 1; 73 74 /* Assign presentation context id */ 75 pce = &bhdr->p_context_elem.p_cont_elem[0]; 76 pce->p_cont_id = clnt->next_p_cont_id++; 77 pce->n_transfer_syn = 1; 78 79 /* Set up UUIDs and versions from the service */ 80 pce->abstract_syntax.if_version = msvc->abstract_syntax_version; 81 rc = ndr_uuid_parse(msvc->abstract_syntax_uuid, 82 &pce->abstract_syntax.if_uuid); 83 if (rc != 0) 84 return (NDR_DRC_FAULT_API_SERVICE_INVALID); 85 86 pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version; 87 rc = ndr_uuid_parse(msvc->transfer_syntax_uuid, 88 &pce->transfer_syntaxes[0].if_uuid); 89 if (rc != 0) 90 return (NDR_DRC_FAULT_API_SERVICE_INVALID); 91 92 /* Format and exchange the PDU */ 93 94 if ((*clnt->xa_init)(clnt, &mxa) < 0) 95 return (NDR_DRC_FAULT_OUT_OF_MEMORY); 96 97 rc = ndr_encode_pdu_hdr(&mxa); 98 if (NDR_DRC_IS_FAULT(rc)) 99 goto fault_exit; 100 101 if ((*clnt->xa_exchange)(clnt, &mxa) < 0) { 102 rc = NDR_DRC_FAULT_SEND_FAILED; 103 goto fault_exit; 104 } 105 106 rc = ndr_decode_pdu_hdr(&mxa); 107 if (NDR_DRC_IS_FAULT(rc)) 108 goto fault_exit; 109 110 /* done with buffers */ 111 (*clnt->xa_destruct)(clnt, &mxa); 112 113 bahdr = &mxa.recv_hdr.bind_ack_hdr; 114 115 if (mxa.ptype != NDR_PTYPE_BIND_ACK) 116 return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 117 118 if (bahdr->p_result_list.n_results != 1) 119 return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 120 121 pre = &bahdr->p_result_list.p_results[0]; 122 123 if (pre->result != NDR_PCDR_ACCEPTANCE) 124 return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 125 126 mbind->p_cont_id = pce->p_cont_id; 127 mbind->which_side = NDR_BIND_SIDE_CLIENT; 128 mbind->clnt = clnt; 129 mbind->service = msvc; 130 mbind->instance_specific = 0; 131 132 *ret_binding_p = mbind; 133 return (NDR_DRC_OK); 134 135 fault_exit: 136 (*clnt->xa_destruct)(clnt, &mxa); 137 return (rc); 138 } 139 140 int 141 ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params) 142 { 143 ndr_client_t *clnt = mbind->clnt; 144 ndr_service_t *msvc = mbind->service; 145 ndr_xa_t mxa; 146 ndr_request_hdr_t *reqhdr; 147 ndr_common_header_t *rsphdr; 148 unsigned long recv_pdu_scan_offset; 149 int rc; 150 151 if (ndr_svc_lookup_name(msvc->name) == NULL) 152 return (NDR_DRC_FAULT_API_SERVICE_INVALID); 153 154 bzero(&mxa, sizeof (mxa)); 155 mxa.ptype = NDR_PTYPE_REQUEST; 156 mxa.opnum = opnum; 157 mxa.binding = mbind; 158 159 ndr_clnt_init_hdr(clnt, &mxa); 160 161 reqhdr = &mxa.send_hdr.request_hdr; 162 reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST; 163 reqhdr->p_cont_id = mbind->p_cont_id; 164 reqhdr->opnum = opnum; 165 166 rc = (*clnt->xa_init)(clnt, &mxa); 167 if (NDR_DRC_IS_FAULT(rc)) 168 return (rc); 169 170 /* Reserve room for hdr */ 171 mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr); 172 173 rc = ndr_encode_call(&mxa, params); 174 if (!NDR_DRC_IS_OK(rc)) 175 goto fault_exit; 176 177 mxa.send_nds.pdu_scan_offset = 0; 178 179 /* 180 * Now we have the PDU size, we need to set up the 181 * frag_length and calculate the alloc_hint. 182 */ 183 mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size; 184 reqhdr->alloc_hint = mxa.send_nds.pdu_size - 185 sizeof (ndr_request_hdr_t); 186 187 rc = ndr_encode_pdu_hdr(&mxa); 188 if (NDR_DRC_IS_FAULT(rc)) 189 goto fault_exit; 190 191 rc = (*clnt->xa_exchange)(clnt, &mxa); 192 if (NDR_DRC_IS_FAULT(rc)) 193 goto fault_exit; 194 195 rc = ndr_decode_pdu_hdr(&mxa); 196 if (NDR_DRC_IS_FAULT(rc)) 197 goto fault_exit; 198 199 if (mxa.ptype != NDR_PTYPE_RESPONSE) { 200 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 201 goto fault_exit; 202 } 203 204 rsphdr = &mxa.recv_hdr.common_hdr; 205 206 if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) { 207 /* 208 * This is a multi-fragment response. 209 * Preserve the current scan offset while getting 210 * fragments so that we can continue afterward 211 * as if we had received the entire response as 212 * a single PDU. 213 */ 214 (void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL); 215 216 recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset; 217 mxa.recv_nds.pdu_scan_offset = rsphdr->frag_length; 218 mxa.recv_nds.pdu_size = rsphdr->frag_length; 219 220 if (ndr_clnt_get_frags(clnt, &mxa) < 0) { 221 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 222 goto fault_exit; 223 } 224 225 mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset; 226 } 227 228 rc = ndr_decode_return(&mxa, params); 229 if (NDR_DRC_IS_FAULT(rc)) 230 goto fault_exit; 231 232 (*clnt->xa_preserve)(clnt, &mxa); 233 (*clnt->xa_destruct)(clnt, &mxa); 234 return (NDR_DRC_OK); 235 236 fault_exit: 237 (*clnt->xa_destruct)(clnt, &mxa); 238 return (rc); 239 } 240 241 void 242 ndr_clnt_free_heap(ndr_client_t *clnt) 243 { 244 (*clnt->xa_release)(clnt); 245 } 246 247 static void 248 ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa) 249 { 250 ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; 251 252 hdr->rpc_vers = 5; 253 hdr->rpc_vers_minor = 0; 254 hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; 255 hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII; 256 #ifndef _BIG_ENDIAN 257 hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN; 258 #endif 259 /* hdr->frag_length */ 260 hdr->auth_length = 0; 261 hdr->call_id = clnt->next_call_id++; 262 } 263 264 /* 265 * ndr_clnt_remove_hdr 266 * 267 * Remove an RPC fragment header from the received data stream. 268 * 269 * Original RPC receive buffer: 270 * |- frag1 -| |-frag M(partial)-| 271 * +==================+=============+----+=================+ 272 * | SmbTransact Rsp1 | SmbTransact | | SmbReadX RspN | 273 * | (with RPC hdr) | Rsp2 | .. | (with RPC hdr) | 274 * +-----+------------+-------------+ +-----+-----------+ 275 * | hdr | data | data | .. | hdr | data | 276 * +=====+============+=============+----+=====+===========+ 277 * <------ 278 * ^ ^ ^ 279 * | | | 280 * base_offset hdr data 281 * 282 * |-------------------------------------|-----------------| 283 * scan_offset len 284 * 285 * RPC receive buffer (after this call): 286 * +==================+=============+----+===========+ 287 * | SmbTransact Rsp1 | SmbTransact | | SmbReadX | 288 * | (with RPC hdr) | Rsp2 | .. | RspN | 289 * +-----+------------+-------------+ +-----------+ 290 * | hdr | data | data | .. | data | 291 * +=====+============+=============+----+===========+ 292 */ 293 static void 294 ndr_clnt_remove_hdr(ndr_stream_t *nds) 295 { 296 char *hdr; 297 char *data; 298 int nbytes; 299 300 hdr = (char *)nds->pdu_base_offset + nds->pdu_scan_offset; 301 data = hdr + NDR_RSP_HDR_SIZE; 302 nbytes = nds->pdu_size - nds->pdu_scan_offset - NDR_RSP_HDR_SIZE; 303 304 bcopy(data, hdr, nbytes); 305 nds->pdu_size -= NDR_RSP_HDR_SIZE; 306 } 307 308 /* 309 * ndr_clnt_get_frags 310 * 311 * A DCE RPC message that is larger than a single fragment is transmitted 312 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for 313 * both Windows 2000 and 2003. 314 * 315 * Collect RPC fragments and append them to the receive stream buffer. 316 * Each received fragment has a header, which we need to remove as we 317 * build the full RPC PDU. The scan offset is used to track frag headers. 318 */ 319 static int 320 ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa) 321 { 322 ndr_stream_t *nds = &mxa->recv_nds; 323 ndr_common_header_t hdr; 324 int frag_size; 325 int last_frag; 326 327 do { 328 if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) { 329 nds_show_state(nds); 330 return (-1); 331 } 332 333 last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags); 334 frag_size = hdr.frag_length; 335 336 if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) { 337 nds_show_state(nds); 338 return (-1); 339 } 340 341 ndr_clnt_remove_hdr(nds); 342 nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE; 343 } while (!last_frag); 344 345 return (0); 346 } 347 348 /* 349 * Read the next RPC fragment. The xa_read() calls correspond to SmbReadX 350 * requests. Note that there is no correspondence between SmbReadX buffering 351 * and DCE RPC fragment alignment. 352 */ 353 static int 354 ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr) 355 { 356 ndr_stream_t *nds = &mxa->recv_nds; 357 unsigned long available; 358 int nbytes = 0; 359 360 available = nds->pdu_size - nds->pdu_scan_offset; 361 362 while (available < NDR_RSP_HDR_SIZE) { 363 if ((nbytes += (*clnt->xa_read)(clnt, mxa)) <= 0) 364 return (-1); 365 available += nbytes; 366 } 367 368 ndr_decode_frag_hdr(nds, hdr); 369 ndr_show_hdr(hdr); 370 371 while (available < hdr->frag_length) { 372 if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0) 373 return (-1); 374 available += nbytes; 375 } 376 377 return (nbytes); 378 } 379