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