15772Sas200622 /* 25772Sas200622 * CDDL HEADER START 35772Sas200622 * 45772Sas200622 * The contents of this file are subject to the terms of the 55772Sas200622 * Common Development and Distribution License (the "License"). 65772Sas200622 * You may not use this file except in compliance with the License. 75772Sas200622 * 85772Sas200622 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 95772Sas200622 * or http://www.opensolaris.org/os/licensing. 105772Sas200622 * See the License for the specific language governing permissions 115772Sas200622 * and limitations under the License. 125772Sas200622 * 135772Sas200622 * When distributing Covered Code, include this CDDL HEADER in each 145772Sas200622 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 155772Sas200622 * If applicable, add the following below this CDDL HEADER, with the 165772Sas200622 * fields enclosed by brackets "[]" replaced with your own identifying 175772Sas200622 * information: Portions Copyright [yyyy] [name of copyright owner] 185772Sas200622 * 195772Sas200622 * CDDL HEADER END 205772Sas200622 */ 215772Sas200622 /* 227619SJose.Borrego@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 235772Sas200622 * Use is subject to license terms. 245772Sas200622 */ 255772Sas200622 265772Sas200622 #include <sys/errno.h> 275772Sas200622 #include <string.h> 285772Sas200622 #include <strings.h> 295772Sas200622 305772Sas200622 #include <smbsrv/libsmb.h> 31*8334SJose.Borrego@Sun.COM #include <smbsrv/libmlrpc.h> 325772Sas200622 33*8334SJose.Borrego@Sun.COM #define NDR_IS_LAST_FRAG(F) ((F) & NDR_PFC_LAST_FRAG) 34*8334SJose.Borrego@Sun.COM #define NDR_DEFAULT_FRAGSZ 8192 355772Sas200622 36*8334SJose.Borrego@Sun.COM static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_t *); 37*8334SJose.Borrego@Sun.COM static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *); 38*8334SJose.Borrego@Sun.COM static void ndr_clnt_remove_hdr(ndr_stream_t *, int *); 395772Sas200622 405772Sas200622 int 41*8334SJose.Borrego@Sun.COM ndr_clnt_bind(ndr_client_t *clnt, const char *service_name, 42*8334SJose.Borrego@Sun.COM ndr_binding_t **ret_binding_p) 435772Sas200622 { 44*8334SJose.Borrego@Sun.COM ndr_service_t *msvc; 45*8334SJose.Borrego@Sun.COM ndr_binding_t *mbind; 46*8334SJose.Borrego@Sun.COM ndr_xa_t mxa; 477619SJose.Borrego@Sun.COM ndr_bind_hdr_t *bhdr; 48*8334SJose.Borrego@Sun.COM ndr_p_cont_elem_t *pce; 497619SJose.Borrego@Sun.COM ndr_bind_ack_hdr_t *bahdr; 50*8334SJose.Borrego@Sun.COM ndr_p_result_t *pre; 515772Sas200622 int rc; 525772Sas200622 535772Sas200622 bzero(&mxa, sizeof (mxa)); 545772Sas200622 55*8334SJose.Borrego@Sun.COM msvc = ndr_svc_lookup_name(service_name); 565772Sas200622 if (msvc == NULL) 57*8334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID); 585772Sas200622 59*8334SJose.Borrego@Sun.COM mxa.binding_list = clnt->binding_list; 60*8334SJose.Borrego@Sun.COM if ((mbind = ndr_svc_new_binding(&mxa)) == NULL) 61*8334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_BIND_NO_SLOTS); 625772Sas200622 63*8334SJose.Borrego@Sun.COM ndr_clnt_init_hdr(clnt, &mxa); 645772Sas200622 655772Sas200622 bhdr = &mxa.send_hdr.bind_hdr; 66*8334SJose.Borrego@Sun.COM bhdr->common_hdr.ptype = NDR_PTYPE_BIND; 675772Sas200622 bhdr->common_hdr.frag_length = sizeof (*bhdr); 68*8334SJose.Borrego@Sun.COM bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ; 69*8334SJose.Borrego@Sun.COM bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ; 705772Sas200622 bhdr->assoc_group_id = 0; 715772Sas200622 bhdr->p_context_elem.n_context_elem = 1; 725772Sas200622 735772Sas200622 /* Assign presentation context id */ 745772Sas200622 pce = &bhdr->p_context_elem.p_cont_elem[0]; 75*8334SJose.Borrego@Sun.COM pce->p_cont_id = clnt->next_p_cont_id++; 765772Sas200622 pce->n_transfer_syn = 1; 775772Sas200622 785772Sas200622 /* Set up UUIDs and versions from the service */ 795772Sas200622 pce->abstract_syntax.if_version = msvc->abstract_syntax_version; 80*8334SJose.Borrego@Sun.COM rc = ndr_uuid_parse(msvc->abstract_syntax_uuid, 815772Sas200622 &pce->abstract_syntax.if_uuid); 82*8334SJose.Borrego@Sun.COM if (rc != 0) 83*8334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID); 845772Sas200622 855772Sas200622 pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version; 86*8334SJose.Borrego@Sun.COM rc = ndr_uuid_parse(msvc->transfer_syntax_uuid, 875772Sas200622 &pce->transfer_syntaxes[0].if_uuid); 88*8334SJose.Borrego@Sun.COM if (rc != 0) 89*8334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID); 905772Sas200622 915772Sas200622 /* Format and exchange the PDU */ 925772Sas200622 93*8334SJose.Borrego@Sun.COM if ((*clnt->xa_init)(clnt, &mxa) < 0) 94*8334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_OUT_OF_MEMORY); 955772Sas200622 96*8334SJose.Borrego@Sun.COM rc = ndr_encode_pdu_hdr(&mxa); 97*8334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 985772Sas200622 goto fault_exit; 995772Sas200622 100*8334SJose.Borrego@Sun.COM if ((*clnt->xa_exchange)(clnt, &mxa) < 0) { 101*8334SJose.Borrego@Sun.COM rc = NDR_DRC_FAULT_SEND_FAILED; 1025772Sas200622 goto fault_exit; 103*8334SJose.Borrego@Sun.COM } 1045772Sas200622 105*8334SJose.Borrego@Sun.COM rc = ndr_decode_pdu_hdr(&mxa); 106*8334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1075772Sas200622 goto fault_exit; 1085772Sas200622 1095772Sas200622 /* done with buffers */ 110*8334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa); 1115772Sas200622 1125772Sas200622 bahdr = &mxa.recv_hdr.bind_ack_hdr; 1135772Sas200622 114*8334SJose.Borrego@Sun.COM if (mxa.ptype != NDR_PTYPE_BIND_ACK) 115*8334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 1165772Sas200622 1175772Sas200622 if (bahdr->p_result_list.n_results != 1) 118*8334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 1195772Sas200622 1205772Sas200622 pre = &bahdr->p_result_list.p_results[0]; 1215772Sas200622 122*8334SJose.Borrego@Sun.COM if (pre->result != NDR_PCDR_ACCEPTANCE) 123*8334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 1245772Sas200622 1255772Sas200622 mbind->p_cont_id = pce->p_cont_id; 126*8334SJose.Borrego@Sun.COM mbind->which_side = NDR_BIND_SIDE_CLIENT; 127*8334SJose.Borrego@Sun.COM mbind->clnt = clnt; 1285772Sas200622 mbind->service = msvc; 1295772Sas200622 mbind->instance_specific = 0; 1305772Sas200622 1315772Sas200622 *ret_binding_p = mbind; 132*8334SJose.Borrego@Sun.COM return (NDR_DRC_OK); 1335772Sas200622 1345772Sas200622 fault_exit: 135*8334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa); 1365772Sas200622 return (rc); 1375772Sas200622 } 1385772Sas200622 1395772Sas200622 int 140*8334SJose.Borrego@Sun.COM ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params) 1415772Sas200622 { 142*8334SJose.Borrego@Sun.COM ndr_client_t *clnt = mbind->clnt; 143*8334SJose.Borrego@Sun.COM ndr_service_t *msvc = mbind->service; 144*8334SJose.Borrego@Sun.COM ndr_xa_t mxa; 1457619SJose.Borrego@Sun.COM ndr_request_hdr_t *reqhdr; 1467619SJose.Borrego@Sun.COM ndr_common_header_t *rsphdr; 147*8334SJose.Borrego@Sun.COM unsigned long recv_pdu_scan_offset; 1485772Sas200622 int rc; 1495772Sas200622 150*8334SJose.Borrego@Sun.COM if (ndr_svc_find_stub(msvc, opnum) == NULL) 151*8334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_OPNUM_INVALID); 1525772Sas200622 1535772Sas200622 bzero(&mxa, sizeof (mxa)); 154*8334SJose.Borrego@Sun.COM mxa.ptype = NDR_PTYPE_REQUEST; 1555772Sas200622 mxa.opnum = opnum; 1565772Sas200622 mxa.binding = mbind; 1575772Sas200622 158*8334SJose.Borrego@Sun.COM ndr_clnt_init_hdr(clnt, &mxa); 1595772Sas200622 1605772Sas200622 reqhdr = &mxa.send_hdr.request_hdr; 161*8334SJose.Borrego@Sun.COM reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST; 1625772Sas200622 reqhdr->p_cont_id = mbind->p_cont_id; 1635772Sas200622 reqhdr->opnum = opnum; 1645772Sas200622 165*8334SJose.Borrego@Sun.COM rc = (*clnt->xa_init)(clnt, &mxa); 166*8334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1675772Sas200622 return (rc); 1685772Sas200622 1695772Sas200622 /* Reserve room for hdr */ 170*8334SJose.Borrego@Sun.COM mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr); 1715772Sas200622 172*8334SJose.Borrego@Sun.COM rc = ndr_encode_call(&mxa, params); 173*8334SJose.Borrego@Sun.COM if (!NDR_DRC_IS_OK(rc)) 1745772Sas200622 goto fault_exit; 1755772Sas200622 176*8334SJose.Borrego@Sun.COM mxa.send_nds.pdu_scan_offset = 0; 1775772Sas200622 1785772Sas200622 /* 1795772Sas200622 * Now we have the PDU size, we need to set up the 1805772Sas200622 * frag_length and calculate the alloc_hint. 1815772Sas200622 */ 182*8334SJose.Borrego@Sun.COM mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size; 183*8334SJose.Borrego@Sun.COM reqhdr->alloc_hint = mxa.send_nds.pdu_size - 1847619SJose.Borrego@Sun.COM sizeof (ndr_request_hdr_t); 1855772Sas200622 186*8334SJose.Borrego@Sun.COM rc = ndr_encode_pdu_hdr(&mxa); 187*8334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1885772Sas200622 goto fault_exit; 1895772Sas200622 190*8334SJose.Borrego@Sun.COM rc = (*clnt->xa_exchange)(clnt, &mxa); 191*8334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1925772Sas200622 goto fault_exit; 1935772Sas200622 194*8334SJose.Borrego@Sun.COM rc = ndr_decode_pdu_hdr(&mxa); 195*8334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1965772Sas200622 goto fault_exit; 1975772Sas200622 198*8334SJose.Borrego@Sun.COM if (mxa.ptype != NDR_PTYPE_RESPONSE) { 199*8334SJose.Borrego@Sun.COM rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 2005772Sas200622 goto fault_exit; 2015772Sas200622 } 2025772Sas200622 2035772Sas200622 rsphdr = &mxa.recv_hdr.common_hdr; 2045772Sas200622 205*8334SJose.Borrego@Sun.COM if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) { 2065772Sas200622 /* 2075772Sas200622 * This is a multi-fragment response. 2085772Sas200622 * Preserve the current scan offset while getting 2095772Sas200622 * fragments so that we can continue afterward 2105772Sas200622 * as if we had received the entire response as 2115772Sas200622 * a single PDU. 2125772Sas200622 */ 213*8334SJose.Borrego@Sun.COM recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset; 2145772Sas200622 215*8334SJose.Borrego@Sun.COM if (ndr_clnt_get_frags(clnt, &mxa) < 0) { 216*8334SJose.Borrego@Sun.COM rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 2175772Sas200622 goto fault_exit; 2185772Sas200622 } 2195772Sas200622 220*8334SJose.Borrego@Sun.COM mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset; 2215772Sas200622 } 2225772Sas200622 223*8334SJose.Borrego@Sun.COM rc = ndr_decode_return(&mxa, params); 224*8334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 2255772Sas200622 goto fault_exit; 2265772Sas200622 227*8334SJose.Borrego@Sun.COM (*clnt->xa_preserve)(clnt, &mxa); 228*8334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa); 229*8334SJose.Borrego@Sun.COM return (NDR_DRC_OK); 2305772Sas200622 2315772Sas200622 fault_exit: 232*8334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa); 2335772Sas200622 return (rc); 2345772Sas200622 } 2355772Sas200622 2365772Sas200622 void 237*8334SJose.Borrego@Sun.COM ndr_clnt_free_heap(ndr_client_t *clnt) 2385772Sas200622 { 239*8334SJose.Borrego@Sun.COM (*clnt->xa_release)(clnt); 2405772Sas200622 } 2415772Sas200622 2425772Sas200622 static void 243*8334SJose.Borrego@Sun.COM ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa) 2445772Sas200622 { 2457619SJose.Borrego@Sun.COM ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; 2465772Sas200622 2475772Sas200622 hdr->rpc_vers = 5; 2485772Sas200622 hdr->rpc_vers_minor = 0; 249*8334SJose.Borrego@Sun.COM hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; 250*8334SJose.Borrego@Sun.COM hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII; 2515772Sas200622 #ifndef _BIG_ENDIAN 252*8334SJose.Borrego@Sun.COM hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN; 2535772Sas200622 #endif 2545772Sas200622 /* hdr->frag_length */ 2555772Sas200622 hdr->auth_length = 0; 256*8334SJose.Borrego@Sun.COM hdr->call_id = clnt->next_call_id++; 2575772Sas200622 } 2585772Sas200622 2595772Sas200622 /* 260*8334SJose.Borrego@Sun.COM * ndr_clnt_remove_hdr 2615772Sas200622 * 2625772Sas200622 * Remove an RPC fragment header from the received data stream. 2635772Sas200622 * 2645772Sas200622 * Original RPC receive buffer: 2655772Sas200622 * |- frag1 -| |-frag M(partial)-| 2665772Sas200622 * +==================+=============+----+=================+ 2675772Sas200622 * | SmbTransact Rsp1 | SmbTransact | | SmbReadX RspN | 2685772Sas200622 * | (with RPC hdr) | Rsp2 | .. | (with RPC hdr) | 2695772Sas200622 * +-----+------------+-------------+ +-----+-----------+ 2705772Sas200622 * | hdr | data | data | .. | hdr | data | 2715772Sas200622 * +=====+============+=============+----+=====+===========+ 2725772Sas200622 * <------ 2735772Sas200622 * ^ ^ ^ 2745772Sas200622 * | | | 2755772Sas200622 * base_offset hdr data 2765772Sas200622 * 2775772Sas200622 * |-------------------------------------|-----------------| 2785772Sas200622 * offset len 2795772Sas200622 * 2805772Sas200622 * RPC receive buffer (after this call): 2815772Sas200622 * +==================+=============+----+===========+ 2825772Sas200622 * | SmbTransact Rsp1 | SmbTransact | | SmbReadX | 2835772Sas200622 * | (with RPC hdr) | Rsp2 | .. | RspN | 2845772Sas200622 * +-----+------------+-------------+ +-----------+ 2855772Sas200622 * | hdr | data | data | .. | data | 2865772Sas200622 * +=====+============+=============+----+===========+ 2875772Sas200622 */ 2885772Sas200622 static void 289*8334SJose.Borrego@Sun.COM ndr_clnt_remove_hdr(ndr_stream_t *nds, int *nbytes) 2905772Sas200622 { 2915772Sas200622 char *hdr; 2925772Sas200622 char *data; 2935772Sas200622 294*8334SJose.Borrego@Sun.COM hdr = (char *)nds->pdu_base_offset + nds->pdu_scan_offset; 295*8334SJose.Borrego@Sun.COM data = hdr + NDR_RSP_HDR_SIZE; 296*8334SJose.Borrego@Sun.COM *nbytes -= NDR_RSP_HDR_SIZE; 2975772Sas200622 2985772Sas200622 bcopy(data, hdr, *nbytes); 299*8334SJose.Borrego@Sun.COM nds->pdu_size -= NDR_RSP_HDR_SIZE; 3005772Sas200622 } 3015772Sas200622 3025772Sas200622 /* 303*8334SJose.Borrego@Sun.COM * ndr_clnt_get_frags 3045772Sas200622 * 3055772Sas200622 * A DCE RPC message that is larger than a single fragment is transmitted 3065772Sas200622 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for 3075772Sas200622 * both Windows 2000 and 2003. 3085772Sas200622 * 3095772Sas200622 * Collect RPC fragments and append them to the receive stream buffer. 3105772Sas200622 * Each received fragment has a header, which we need to remove as we 3115772Sas200622 * build the full RPC PDU. 3125772Sas200622 * 3135772Sas200622 * The xa_read() calls will translate to SmbReadX requests. Note that 3145772Sas200622 * there is no correspondence between SmbReadX buffering and DCE RPC 3155772Sas200622 * fragment alignment. 3165772Sas200622 * 3175772Sas200622 * Return -1 on error. Otherwise, return the total data count of the 3185772Sas200622 * complete RPC response upon success. 3195772Sas200622 */ 3205772Sas200622 static int 321*8334SJose.Borrego@Sun.COM ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa) 3225772Sas200622 { 323*8334SJose.Borrego@Sun.COM ndr_stream_t *nds = &mxa->recv_nds; 3247619SJose.Borrego@Sun.COM ndr_common_header_t hdr; 3255772Sas200622 int frag_rcvd; 3265772Sas200622 int frag_size; 3275772Sas200622 int last_frag; 3285772Sas200622 int nbytes; 3295772Sas200622 3305772Sas200622 /* 3315772Sas200622 * The scan offest will be used to locate the frag header. 3325772Sas200622 */ 333*8334SJose.Borrego@Sun.COM nds->pdu_scan_offset = nds->pdu_base_offset + nds->pdu_size; 3345772Sas200622 3355772Sas200622 do { 3365772Sas200622 frag_rcvd = 0; 3375772Sas200622 3385772Sas200622 do { 339*8334SJose.Borrego@Sun.COM if ((nbytes = (*clnt->xa_read)(clnt, mxa)) < 0) 3405772Sas200622 return (-1); 3415772Sas200622 3425772Sas200622 if (frag_rcvd == 0) { 343*8334SJose.Borrego@Sun.COM ndr_decode_frag_hdr(nds, &hdr); 3445772Sas200622 345*8334SJose.Borrego@Sun.COM last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags); 346*8334SJose.Borrego@Sun.COM frag_size = hdr.frag_length - NDR_RSP_HDR_SIZE; 3475772Sas200622 348*8334SJose.Borrego@Sun.COM ndr_clnt_remove_hdr(nds, &nbytes); 349*8334SJose.Borrego@Sun.COM nds->pdu_scan_offset += frag_size; 3505772Sas200622 } 3515772Sas200622 3525772Sas200622 frag_rcvd += nbytes; 3535772Sas200622 3545772Sas200622 } while (frag_rcvd < frag_size); 3555772Sas200622 } while (!last_frag); 3565772Sas200622 3575772Sas200622 return (0); 3585772Sas200622 } 359