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 2010 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 int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *); 38 static int ndr_clnt_get_frag(ndr_client_t *, ndr_xa_t *, ndr_common_header_t *); 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_lookup_name(msvc->name) == NULL) 151 return (NDR_DRC_FAULT_API_SERVICE_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 (void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL); 214 215 recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset; 216 mxa.recv_nds.pdu_scan_offset = rsphdr->frag_length; 217 mxa.recv_nds.pdu_size = rsphdr->frag_length; 218 219 if (ndr_clnt_get_frags(clnt, &mxa) < 0) { 220 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 221 goto fault_exit; 222 } 223 224 mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset; 225 } 226 227 rc = ndr_decode_return(&mxa, params); 228 if (NDR_DRC_IS_FAULT(rc)) 229 goto fault_exit; 230 231 (*clnt->xa_preserve)(clnt, &mxa); 232 (*clnt->xa_destruct)(clnt, &mxa); 233 return (NDR_DRC_OK); 234 235 fault_exit: 236 (*clnt->xa_destruct)(clnt, &mxa); 237 return (rc); 238 } 239 240 void 241 ndr_clnt_free_heap(ndr_client_t *clnt) 242 { 243 (*clnt->xa_release)(clnt); 244 } 245 246 static void 247 ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa) 248 { 249 ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; 250 251 hdr->rpc_vers = 5; 252 hdr->rpc_vers_minor = 0; 253 hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; 254 hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII; 255 #ifndef _BIG_ENDIAN 256 hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN; 257 #endif 258 /* hdr->frag_length */ 259 hdr->auth_length = 0; 260 hdr->call_id = clnt->next_call_id++; 261 } 262 263 /* 264 * ndr_clnt_get_frags 265 * 266 * A DCE RPC message that is larger than a single fragment is transmitted 267 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for 268 * both Windows 2000 and 2003. 269 * 270 * Collect RPC fragments and append them to the receive stream buffer. 271 * Each received fragment has a header, which we need to remove as we 272 * build the full RPC PDU. The scan offset is used to track frag headers. 273 */ 274 static int 275 ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa) 276 { 277 ndr_stream_t *nds = &mxa->recv_nds; 278 ndr_common_header_t hdr; 279 int frag_size; 280 int last_frag; 281 282 do { 283 if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) { 284 nds_show_state(nds); 285 return (-1); 286 } 287 288 last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags); 289 frag_size = hdr.frag_length; 290 291 if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) { 292 nds_show_state(nds); 293 return (-1); 294 } 295 296 ndr_remove_frag_hdr(nds); 297 nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE; 298 } while (!last_frag); 299 300 return (0); 301 } 302 303 /* 304 * Read the next RPC fragment. The xa_read() calls correspond to SmbReadX 305 * requests. Note that there is no correspondence between SmbReadX buffering 306 * and DCE RPC fragment alignment. 307 */ 308 static int 309 ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr) 310 { 311 ndr_stream_t *nds = &mxa->recv_nds; 312 unsigned long available; 313 int nbytes = 0; 314 315 available = nds->pdu_size - nds->pdu_scan_offset; 316 317 while (available < NDR_RSP_HDR_SIZE) { 318 if ((nbytes += (*clnt->xa_read)(clnt, mxa)) <= 0) 319 return (-1); 320 available += nbytes; 321 } 322 323 ndr_decode_frag_hdr(nds, hdr); 324 ndr_show_hdr(hdr); 325 326 while (available < hdr->frag_length) { 327 if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0) 328 return (-1); 329 available += nbytes; 330 } 331 332 return (nbytes); 333 } 334