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