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/ndr.h> 32 #include <smbsrv/mlrpc.h> 33 34 #define MLRPC_IS_LAST_FRAG(F) ((F) & MLRPC_PFC_LAST_FRAG) 35 #define MLRPC_DEFAULT_FRAGSZ 8192 36 37 static void mlrpc_c_init_hdr(struct mlrpc_client *, struct mlrpc_xaction *); 38 static int mlrpc_c_get_frags(struct mlrpc_client *, struct mlrpc_xaction *); 39 static void mlrpc_c_remove_hdr(struct mlndr_stream *, int *); 40 41 int 42 mlrpc_c_bind(struct mlrpc_client *mcli, char *service_name, 43 struct mlrpc_binding **ret_binding_p) 44 { 45 struct mlrpc_service *msvc; 46 struct mlrpc_binding *mbind; 47 struct mlrpc_xaction mxa; 48 ndr_bind_hdr_t *bhdr; 49 mlrpc_p_cont_elem_t *pce; 50 ndr_bind_ack_hdr_t *bahdr; 51 mlrpc_p_result_t *pre; 52 int rc; 53 54 bzero(&mxa, sizeof (mxa)); 55 56 msvc = mlrpc_find_service_by_name(service_name); 57 if (msvc == NULL) 58 return (MLRPC_DRC_FAULT_API_SERVICE_INVALID); 59 60 mxa.binding_list = mcli->binding_list; 61 if ((mbind = mlrpc_new_binding(&mxa)) == NULL) 62 return (MLRPC_DRC_FAULT_API_BIND_NO_SLOTS); 63 64 mlrpc_c_init_hdr(mcli, &mxa); 65 66 bhdr = &mxa.send_hdr.bind_hdr; 67 bhdr->common_hdr.ptype = MLRPC_PTYPE_BIND; 68 bhdr->common_hdr.frag_length = sizeof (*bhdr); 69 bhdr->max_xmit_frag = MLRPC_DEFAULT_FRAGSZ; 70 bhdr->max_recv_frag = MLRPC_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 = mcli->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 = mlrpc_str_to_uuid(msvc->abstract_syntax_uuid, 82 &pce->abstract_syntax.if_uuid); 83 if (!rc) 84 return (MLRPC_DRC_FAULT_API_SERVICE_INVALID); 85 86 pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version; 87 rc = mlrpc_str_to_uuid(msvc->transfer_syntax_uuid, 88 &pce->transfer_syntaxes[0].if_uuid); 89 if (!rc) 90 return (MLRPC_DRC_FAULT_API_SERVICE_INVALID); 91 92 /* Format and exchange the PDU */ 93 94 rc = (*mcli->xa_init)(mcli, &mxa, 0); 95 if (MLRPC_DRC_IS_FAULT(rc)) 96 return (rc); 97 98 rc = mlrpc_encode_pdu_hdr(&mxa); 99 if (MLRPC_DRC_IS_FAULT(rc)) 100 goto fault_exit; 101 102 rc = (*mcli->xa_exchange)(mcli, &mxa); 103 if (MLRPC_DRC_IS_FAULT(rc)) 104 goto fault_exit; 105 106 rc = mlrpc_decode_pdu_hdr(&mxa); 107 if (MLRPC_DRC_IS_FAULT(rc)) 108 goto fault_exit; 109 110 /* done with buffers */ 111 (*mcli->xa_destruct)(mcli, &mxa); 112 113 bahdr = &mxa.recv_hdr.bind_ack_hdr; 114 115 if (mxa.ptype != MLRPC_PTYPE_BIND_ACK) 116 return (MLRPC_DRC_FAULT_RECEIVED_MALFORMED); 117 118 if (bahdr->p_result_list.n_results != 1) 119 return (MLRPC_DRC_FAULT_RECEIVED_MALFORMED); 120 121 pre = &bahdr->p_result_list.p_results[0]; 122 123 if (pre->result != MLRPC_PCDR_ACCEPTANCE) 124 return (MLRPC_DRC_FAULT_RECEIVED_MALFORMED); 125 126 mbind->p_cont_id = pce->p_cont_id; 127 mbind->which_side = MLRPC_BIND_SIDE_CLIENT; 128 mbind->context = mcli; 129 mbind->service = msvc; 130 mbind->instance_specific = 0; 131 132 *ret_binding_p = mbind; 133 return (MLRPC_DRC_OK); 134 135 fault_exit: 136 (*mcli->xa_destruct)(mcli, &mxa); 137 return (rc); 138 } 139 140 int 141 mlrpc_c_call(struct mlrpc_binding *mbind, int opnum, void *params, 142 mlrpc_heapref_t *heapref) 143 { 144 struct mlrpc_client *mcli = mbind->context; 145 struct mlrpc_service *msvc = mbind->service; 146 struct mlrpc_xaction mxa; 147 ndr_request_hdr_t *reqhdr; 148 ndr_common_header_t *rsphdr; 149 unsigned long recv_pdu_scan_offset; 150 int rc; 151 152 if (mlrpc_find_stub_in_svc(msvc, opnum) == NULL) 153 return (MLRPC_DRC_FAULT_API_OPNUM_INVALID); 154 155 bzero(&mxa, sizeof (mxa)); 156 mxa.ptype = MLRPC_PTYPE_REQUEST; 157 mxa.opnum = opnum; 158 mxa.binding = mbind; 159 160 mlrpc_c_init_hdr(mcli, &mxa); 161 162 reqhdr = &mxa.send_hdr.request_hdr; 163 reqhdr->common_hdr.ptype = MLRPC_PTYPE_REQUEST; 164 reqhdr->p_cont_id = mbind->p_cont_id; 165 reqhdr->opnum = opnum; 166 167 rc = (*mcli->xa_init)(mcli, &mxa, heapref->heap); 168 if (MLRPC_DRC_IS_FAULT(rc)) 169 return (rc); 170 171 /* Reserve room for hdr */ 172 mxa.send_mlnds.pdu_scan_offset = sizeof (*reqhdr); 173 174 rc = mlrpc_encode_call(&mxa, params); 175 if (!MLRPC_DRC_IS_OK(rc)) 176 goto fault_exit; 177 178 mxa.send_mlnds.pdu_scan_offset = 0; 179 180 /* 181 * Now we have the PDU size, we need to set up the 182 * frag_length and calculate the alloc_hint. 183 */ 184 mxa.send_hdr.common_hdr.frag_length = mxa.send_mlnds.pdu_size; 185 reqhdr->alloc_hint = mxa.send_mlnds.pdu_size - 186 sizeof (ndr_request_hdr_t); 187 188 rc = mlrpc_encode_pdu_hdr(&mxa); 189 if (MLRPC_DRC_IS_FAULT(rc)) 190 goto fault_exit; 191 192 rc = (*mcli->xa_exchange)(mcli, &mxa); 193 if (MLRPC_DRC_IS_FAULT(rc)) 194 goto fault_exit; 195 196 rc = mlrpc_decode_pdu_hdr(&mxa); 197 if (MLRPC_DRC_IS_FAULT(rc)) 198 goto fault_exit; 199 200 if (mxa.ptype != MLRPC_PTYPE_RESPONSE) { 201 rc = MLRPC_DRC_FAULT_RECEIVED_MALFORMED; 202 goto fault_exit; 203 } 204 205 rsphdr = &mxa.recv_hdr.common_hdr; 206 207 if (!MLRPC_IS_LAST_FRAG(rsphdr->pfc_flags)) { 208 /* 209 * This is a multi-fragment response. 210 * Preserve the current scan offset while getting 211 * fragments so that we can continue afterward 212 * as if we had received the entire response as 213 * a single PDU. 214 */ 215 recv_pdu_scan_offset = mxa.recv_mlnds.pdu_scan_offset; 216 217 if (mlrpc_c_get_frags(mcli, &mxa) < 0) { 218 rc = MLRPC_DRC_FAULT_RECEIVED_MALFORMED; 219 goto fault_exit; 220 } 221 222 mxa.recv_mlnds.pdu_scan_offset = recv_pdu_scan_offset; 223 } 224 225 rc = mlrpc_decode_return(&mxa, params); 226 if (MLRPC_DRC_IS_FAULT(rc)) 227 goto fault_exit; 228 229 rc = (*mcli->xa_preserve)(mcli, &mxa, heapref); 230 if (MLRPC_DRC_IS_FAULT(rc)) 231 goto fault_exit; 232 233 (*mcli->xa_destruct)(mcli, &mxa); 234 return (MLRPC_DRC_OK); 235 236 fault_exit: 237 (*mcli->xa_destruct)(mcli, &mxa); 238 return (rc); 239 } 240 241 void 242 mlrpc_c_free_heap(struct mlrpc_binding *mbind, mlrpc_heapref_t *heapref) 243 { 244 struct mlrpc_client *mcli = mbind->context; 245 246 (*mcli->xa_release)(mcli, heapref); 247 } 248 249 static void 250 mlrpc_c_init_hdr(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa) 251 { 252 ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; 253 254 hdr->rpc_vers = 5; 255 hdr->rpc_vers_minor = 0; 256 hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG; 257 hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII; 258 #ifndef _BIG_ENDIAN 259 hdr->packed_drep.intg_char_rep |= MLRPC_REPLAB_INTG_LITTLE_ENDIAN; 260 #endif 261 /* hdr->frag_length */ 262 hdr->auth_length = 0; 263 hdr->call_id = mcli->next_call_id++; 264 } 265 266 /* 267 * mlrpc_c_remove_hdr 268 * 269 * Remove an RPC fragment header from the received data stream. 270 * 271 * Original RPC receive buffer: 272 * |- frag1 -| |-frag M(partial)-| 273 * +==================+=============+----+=================+ 274 * | SmbTransact Rsp1 | SmbTransact | | SmbReadX RspN | 275 * | (with RPC hdr) | Rsp2 | .. | (with RPC hdr) | 276 * +-----+------------+-------------+ +-----+-----------+ 277 * | hdr | data | data | .. | hdr | data | 278 * +=====+============+=============+----+=====+===========+ 279 * <------ 280 * ^ ^ ^ 281 * | | | 282 * base_offset hdr data 283 * 284 * |-------------------------------------|-----------------| 285 * offset len 286 * 287 * RPC receive buffer (after this call): 288 * +==================+=============+----+===========+ 289 * | SmbTransact Rsp1 | SmbTransact | | SmbReadX | 290 * | (with RPC hdr) | Rsp2 | .. | RspN | 291 * +-----+------------+-------------+ +-----------+ 292 * | hdr | data | data | .. | data | 293 * +=====+============+=============+----+===========+ 294 */ 295 static void 296 mlrpc_c_remove_hdr(struct mlndr_stream *mlnds, int *nbytes) 297 { 298 char *hdr; 299 char *data; 300 301 hdr = (char *)mlnds->pdu_base_offset + mlnds->pdu_scan_offset; 302 data = hdr + MLRPC_RSP_HDR_SIZE; 303 *nbytes -= MLRPC_RSP_HDR_SIZE; 304 305 bcopy(data, hdr, *nbytes); 306 mlnds->pdu_size -= MLRPC_RSP_HDR_SIZE; 307 } 308 309 /* 310 * mlrpc_c_get_frags 311 * 312 * A DCE RPC message that is larger than a single fragment is transmitted 313 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for 314 * both Windows 2000 and 2003. 315 * 316 * Collect RPC fragments and append them to the receive stream buffer. 317 * Each received fragment has a header, which we need to remove as we 318 * build the full RPC PDU. 319 * 320 * The xa_read() calls will translate to SmbReadX requests. Note that 321 * there is no correspondence between SmbReadX buffering and DCE RPC 322 * fragment alignment. 323 * 324 * Return -1 on error. Otherwise, return the total data count of the 325 * complete RPC response upon success. 326 */ 327 static int 328 mlrpc_c_get_frags(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa) 329 { 330 struct mlndr_stream *mlnds = &mxa->recv_mlnds; 331 ndr_common_header_t hdr; 332 int frag_rcvd; 333 int frag_size; 334 int last_frag; 335 int nbytes; 336 337 /* 338 * The scan offest will be used to locate the frag header. 339 */ 340 mlnds->pdu_scan_offset = mlnds->pdu_base_offset + mlnds->pdu_size; 341 342 do { 343 frag_rcvd = 0; 344 345 do { 346 if ((nbytes = (*mcli->xa_read)(mcli, mxa)) < 0) 347 return (-1); 348 349 if (frag_rcvd == 0) { 350 mlrpc_decode_frag_hdr(mlnds, &hdr); 351 352 last_frag = MLRPC_IS_LAST_FRAG(hdr.pfc_flags); 353 frag_size = hdr.frag_length 354 - MLRPC_RSP_HDR_SIZE; 355 356 mlrpc_c_remove_hdr(mlnds, &nbytes); 357 mlnds->pdu_scan_offset += frag_size; 358 } 359 360 frag_rcvd += nbytes; 361 362 } while (frag_rcvd < frag_size); 363 } while (!last_frag); 364 365 return (0); 366 } 367