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 /* 229914Samw@Sun.COM * Copyright 2009 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> 318334SJose.Borrego@Sun.COM #include <smbsrv/libmlrpc.h> 325772Sas200622 338334SJose.Borrego@Sun.COM #define NDR_DEFAULT_FRAGSZ 8192 34*10475Samw@Sun.COM #define NDR_MULTI_FRAGSZ (60 * 1024) 355772Sas200622 368334SJose.Borrego@Sun.COM static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_t *); 37*10475Samw@Sun.COM static void ndr_clnt_remove_hdr(ndr_stream_t *); 388334SJose.Borrego@Sun.COM static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *); 39*10475Samw@Sun.COM static int ndr_clnt_get_frag(ndr_client_t *, ndr_xa_t *, ndr_common_header_t *); 405772Sas200622 415772Sas200622 int 428334SJose.Borrego@Sun.COM ndr_clnt_bind(ndr_client_t *clnt, const char *service_name, 438334SJose.Borrego@Sun.COM ndr_binding_t **ret_binding_p) 445772Sas200622 { 458334SJose.Borrego@Sun.COM ndr_service_t *msvc; 468334SJose.Borrego@Sun.COM ndr_binding_t *mbind; 478334SJose.Borrego@Sun.COM ndr_xa_t mxa; 487619SJose.Borrego@Sun.COM ndr_bind_hdr_t *bhdr; 498334SJose.Borrego@Sun.COM ndr_p_cont_elem_t *pce; 507619SJose.Borrego@Sun.COM ndr_bind_ack_hdr_t *bahdr; 518334SJose.Borrego@Sun.COM ndr_p_result_t *pre; 525772Sas200622 int rc; 535772Sas200622 545772Sas200622 bzero(&mxa, sizeof (mxa)); 555772Sas200622 568334SJose.Borrego@Sun.COM msvc = ndr_svc_lookup_name(service_name); 575772Sas200622 if (msvc == NULL) 588334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID); 595772Sas200622 608334SJose.Borrego@Sun.COM mxa.binding_list = clnt->binding_list; 618334SJose.Borrego@Sun.COM if ((mbind = ndr_svc_new_binding(&mxa)) == NULL) 628334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_BIND_NO_SLOTS); 635772Sas200622 648334SJose.Borrego@Sun.COM ndr_clnt_init_hdr(clnt, &mxa); 655772Sas200622 665772Sas200622 bhdr = &mxa.send_hdr.bind_hdr; 678334SJose.Borrego@Sun.COM bhdr->common_hdr.ptype = NDR_PTYPE_BIND; 685772Sas200622 bhdr->common_hdr.frag_length = sizeof (*bhdr); 698334SJose.Borrego@Sun.COM bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ; 708334SJose.Borrego@Sun.COM bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ; 715772Sas200622 bhdr->assoc_group_id = 0; 725772Sas200622 bhdr->p_context_elem.n_context_elem = 1; 735772Sas200622 745772Sas200622 /* Assign presentation context id */ 755772Sas200622 pce = &bhdr->p_context_elem.p_cont_elem[0]; 768334SJose.Borrego@Sun.COM pce->p_cont_id = clnt->next_p_cont_id++; 775772Sas200622 pce->n_transfer_syn = 1; 785772Sas200622 795772Sas200622 /* Set up UUIDs and versions from the service */ 805772Sas200622 pce->abstract_syntax.if_version = msvc->abstract_syntax_version; 818334SJose.Borrego@Sun.COM rc = ndr_uuid_parse(msvc->abstract_syntax_uuid, 825772Sas200622 &pce->abstract_syntax.if_uuid); 838334SJose.Borrego@Sun.COM if (rc != 0) 848334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID); 855772Sas200622 865772Sas200622 pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version; 878334SJose.Borrego@Sun.COM rc = ndr_uuid_parse(msvc->transfer_syntax_uuid, 885772Sas200622 &pce->transfer_syntaxes[0].if_uuid); 898334SJose.Borrego@Sun.COM if (rc != 0) 908334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID); 915772Sas200622 925772Sas200622 /* Format and exchange the PDU */ 935772Sas200622 948334SJose.Borrego@Sun.COM if ((*clnt->xa_init)(clnt, &mxa) < 0) 958334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_OUT_OF_MEMORY); 965772Sas200622 978334SJose.Borrego@Sun.COM rc = ndr_encode_pdu_hdr(&mxa); 988334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 995772Sas200622 goto fault_exit; 1005772Sas200622 1018334SJose.Borrego@Sun.COM if ((*clnt->xa_exchange)(clnt, &mxa) < 0) { 1028334SJose.Borrego@Sun.COM rc = NDR_DRC_FAULT_SEND_FAILED; 1035772Sas200622 goto fault_exit; 1048334SJose.Borrego@Sun.COM } 1055772Sas200622 1068334SJose.Borrego@Sun.COM rc = ndr_decode_pdu_hdr(&mxa); 1078334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1085772Sas200622 goto fault_exit; 1095772Sas200622 1105772Sas200622 /* done with buffers */ 1118334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa); 1125772Sas200622 1135772Sas200622 bahdr = &mxa.recv_hdr.bind_ack_hdr; 1145772Sas200622 1158334SJose.Borrego@Sun.COM if (mxa.ptype != NDR_PTYPE_BIND_ACK) 1168334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 1175772Sas200622 1185772Sas200622 if (bahdr->p_result_list.n_results != 1) 1198334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 1205772Sas200622 1215772Sas200622 pre = &bahdr->p_result_list.p_results[0]; 1225772Sas200622 1238334SJose.Borrego@Sun.COM if (pre->result != NDR_PCDR_ACCEPTANCE) 1248334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 1255772Sas200622 1265772Sas200622 mbind->p_cont_id = pce->p_cont_id; 1278334SJose.Borrego@Sun.COM mbind->which_side = NDR_BIND_SIDE_CLIENT; 1288334SJose.Borrego@Sun.COM mbind->clnt = clnt; 1295772Sas200622 mbind->service = msvc; 1305772Sas200622 mbind->instance_specific = 0; 1315772Sas200622 1325772Sas200622 *ret_binding_p = mbind; 1338334SJose.Borrego@Sun.COM return (NDR_DRC_OK); 1345772Sas200622 1355772Sas200622 fault_exit: 1368334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa); 1375772Sas200622 return (rc); 1385772Sas200622 } 1395772Sas200622 1405772Sas200622 int 1418334SJose.Borrego@Sun.COM ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params) 1425772Sas200622 { 1438334SJose.Borrego@Sun.COM ndr_client_t *clnt = mbind->clnt; 1448334SJose.Borrego@Sun.COM ndr_service_t *msvc = mbind->service; 1458334SJose.Borrego@Sun.COM ndr_xa_t mxa; 1467619SJose.Borrego@Sun.COM ndr_request_hdr_t *reqhdr; 1477619SJose.Borrego@Sun.COM ndr_common_header_t *rsphdr; 1488334SJose.Borrego@Sun.COM unsigned long recv_pdu_scan_offset; 1495772Sas200622 int rc; 1505772Sas200622 1519914Samw@Sun.COM if (ndr_svc_lookup_name(msvc->name) == NULL) 1529914Samw@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID); 1535772Sas200622 1545772Sas200622 bzero(&mxa, sizeof (mxa)); 1558334SJose.Borrego@Sun.COM mxa.ptype = NDR_PTYPE_REQUEST; 1565772Sas200622 mxa.opnum = opnum; 1575772Sas200622 mxa.binding = mbind; 1585772Sas200622 1598334SJose.Borrego@Sun.COM ndr_clnt_init_hdr(clnt, &mxa); 1605772Sas200622 1615772Sas200622 reqhdr = &mxa.send_hdr.request_hdr; 1628334SJose.Borrego@Sun.COM reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST; 1635772Sas200622 reqhdr->p_cont_id = mbind->p_cont_id; 1645772Sas200622 reqhdr->opnum = opnum; 1655772Sas200622 1668334SJose.Borrego@Sun.COM rc = (*clnt->xa_init)(clnt, &mxa); 1678334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1685772Sas200622 return (rc); 1695772Sas200622 1705772Sas200622 /* Reserve room for hdr */ 1718334SJose.Borrego@Sun.COM mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr); 1725772Sas200622 1738334SJose.Borrego@Sun.COM rc = ndr_encode_call(&mxa, params); 1748334SJose.Borrego@Sun.COM if (!NDR_DRC_IS_OK(rc)) 1755772Sas200622 goto fault_exit; 1765772Sas200622 1778334SJose.Borrego@Sun.COM mxa.send_nds.pdu_scan_offset = 0; 1785772Sas200622 1795772Sas200622 /* 1805772Sas200622 * Now we have the PDU size, we need to set up the 1815772Sas200622 * frag_length and calculate the alloc_hint. 1825772Sas200622 */ 1838334SJose.Borrego@Sun.COM mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size; 1848334SJose.Borrego@Sun.COM reqhdr->alloc_hint = mxa.send_nds.pdu_size - 1857619SJose.Borrego@Sun.COM sizeof (ndr_request_hdr_t); 1865772Sas200622 1878334SJose.Borrego@Sun.COM rc = ndr_encode_pdu_hdr(&mxa); 1888334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1895772Sas200622 goto fault_exit; 1905772Sas200622 1918334SJose.Borrego@Sun.COM rc = (*clnt->xa_exchange)(clnt, &mxa); 1928334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1935772Sas200622 goto fault_exit; 1945772Sas200622 1958334SJose.Borrego@Sun.COM rc = ndr_decode_pdu_hdr(&mxa); 1968334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1975772Sas200622 goto fault_exit; 1985772Sas200622 1998334SJose.Borrego@Sun.COM if (mxa.ptype != NDR_PTYPE_RESPONSE) { 2008334SJose.Borrego@Sun.COM rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 2015772Sas200622 goto fault_exit; 2025772Sas200622 } 2035772Sas200622 2045772Sas200622 rsphdr = &mxa.recv_hdr.common_hdr; 2055772Sas200622 2068334SJose.Borrego@Sun.COM if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) { 2075772Sas200622 /* 2085772Sas200622 * This is a multi-fragment response. 2095772Sas200622 * Preserve the current scan offset while getting 2105772Sas200622 * fragments so that we can continue afterward 2115772Sas200622 * as if we had received the entire response as 2125772Sas200622 * a single PDU. 2135772Sas200622 */ 214*10475Samw@Sun.COM (void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL); 215*10475Samw@Sun.COM 2168334SJose.Borrego@Sun.COM recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset; 217*10475Samw@Sun.COM mxa.recv_nds.pdu_scan_offset = rsphdr->frag_length; 218*10475Samw@Sun.COM mxa.recv_nds.pdu_size = rsphdr->frag_length; 2195772Sas200622 2208334SJose.Borrego@Sun.COM if (ndr_clnt_get_frags(clnt, &mxa) < 0) { 2218334SJose.Borrego@Sun.COM rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 2225772Sas200622 goto fault_exit; 2235772Sas200622 } 2245772Sas200622 2258334SJose.Borrego@Sun.COM mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset; 2265772Sas200622 } 2275772Sas200622 2288334SJose.Borrego@Sun.COM rc = ndr_decode_return(&mxa, params); 2298334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 2305772Sas200622 goto fault_exit; 2315772Sas200622 2328334SJose.Borrego@Sun.COM (*clnt->xa_preserve)(clnt, &mxa); 2338334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa); 2348334SJose.Borrego@Sun.COM return (NDR_DRC_OK); 2355772Sas200622 2365772Sas200622 fault_exit: 2378334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa); 2385772Sas200622 return (rc); 2395772Sas200622 } 2405772Sas200622 2415772Sas200622 void 2428334SJose.Borrego@Sun.COM ndr_clnt_free_heap(ndr_client_t *clnt) 2435772Sas200622 { 2448334SJose.Borrego@Sun.COM (*clnt->xa_release)(clnt); 2455772Sas200622 } 2465772Sas200622 2475772Sas200622 static void 2488334SJose.Borrego@Sun.COM ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa) 2495772Sas200622 { 2507619SJose.Borrego@Sun.COM ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; 2515772Sas200622 2525772Sas200622 hdr->rpc_vers = 5; 2535772Sas200622 hdr->rpc_vers_minor = 0; 2548334SJose.Borrego@Sun.COM hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; 2558334SJose.Borrego@Sun.COM hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII; 2565772Sas200622 #ifndef _BIG_ENDIAN 2578334SJose.Borrego@Sun.COM hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN; 2585772Sas200622 #endif 2595772Sas200622 /* hdr->frag_length */ 2605772Sas200622 hdr->auth_length = 0; 2618334SJose.Borrego@Sun.COM hdr->call_id = clnt->next_call_id++; 2625772Sas200622 } 2635772Sas200622 2645772Sas200622 /* 2658334SJose.Borrego@Sun.COM * ndr_clnt_remove_hdr 2665772Sas200622 * 2675772Sas200622 * Remove an RPC fragment header from the received data stream. 2685772Sas200622 * 2695772Sas200622 * Original RPC receive buffer: 2705772Sas200622 * |- frag1 -| |-frag M(partial)-| 2715772Sas200622 * +==================+=============+----+=================+ 2725772Sas200622 * | SmbTransact Rsp1 | SmbTransact | | SmbReadX RspN | 2735772Sas200622 * | (with RPC hdr) | Rsp2 | .. | (with RPC hdr) | 2745772Sas200622 * +-----+------------+-------------+ +-----+-----------+ 2755772Sas200622 * | hdr | data | data | .. | hdr | data | 2765772Sas200622 * +=====+============+=============+----+=====+===========+ 2775772Sas200622 * <------ 2785772Sas200622 * ^ ^ ^ 2795772Sas200622 * | | | 2805772Sas200622 * base_offset hdr data 2815772Sas200622 * 2825772Sas200622 * |-------------------------------------|-----------------| 283*10475Samw@Sun.COM * scan_offset len 2845772Sas200622 * 2855772Sas200622 * RPC receive buffer (after this call): 2865772Sas200622 * +==================+=============+----+===========+ 2875772Sas200622 * | SmbTransact Rsp1 | SmbTransact | | SmbReadX | 2885772Sas200622 * | (with RPC hdr) | Rsp2 | .. | RspN | 2895772Sas200622 * +-----+------------+-------------+ +-----------+ 2905772Sas200622 * | hdr | data | data | .. | data | 2915772Sas200622 * +=====+============+=============+----+===========+ 2925772Sas200622 */ 2935772Sas200622 static void 294*10475Samw@Sun.COM ndr_clnt_remove_hdr(ndr_stream_t *nds) 2955772Sas200622 { 2965772Sas200622 char *hdr; 2975772Sas200622 char *data; 298*10475Samw@Sun.COM int nbytes; 2995772Sas200622 3008334SJose.Borrego@Sun.COM hdr = (char *)nds->pdu_base_offset + nds->pdu_scan_offset; 3018334SJose.Borrego@Sun.COM data = hdr + NDR_RSP_HDR_SIZE; 302*10475Samw@Sun.COM nbytes = nds->pdu_size - nds->pdu_scan_offset - NDR_RSP_HDR_SIZE; 3035772Sas200622 304*10475Samw@Sun.COM bcopy(data, hdr, nbytes); 3058334SJose.Borrego@Sun.COM nds->pdu_size -= NDR_RSP_HDR_SIZE; 3065772Sas200622 } 3075772Sas200622 3085772Sas200622 /* 3098334SJose.Borrego@Sun.COM * ndr_clnt_get_frags 3105772Sas200622 * 3115772Sas200622 * A DCE RPC message that is larger than a single fragment is transmitted 3125772Sas200622 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for 3135772Sas200622 * both Windows 2000 and 2003. 3145772Sas200622 * 3155772Sas200622 * Collect RPC fragments and append them to the receive stream buffer. 3165772Sas200622 * Each received fragment has a header, which we need to remove as we 317*10475Samw@Sun.COM * build the full RPC PDU. The scan offset is used to track frag headers. 3185772Sas200622 */ 3195772Sas200622 static int 3208334SJose.Borrego@Sun.COM ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa) 3215772Sas200622 { 3228334SJose.Borrego@Sun.COM ndr_stream_t *nds = &mxa->recv_nds; 3237619SJose.Borrego@Sun.COM ndr_common_header_t hdr; 3245772Sas200622 int frag_size; 3255772Sas200622 int last_frag; 3265772Sas200622 3275772Sas200622 do { 328*10475Samw@Sun.COM if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) { 329*10475Samw@Sun.COM nds_show_state(nds); 330*10475Samw@Sun.COM return (-1); 331*10475Samw@Sun.COM } 3325772Sas200622 333*10475Samw@Sun.COM last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags); 334*10475Samw@Sun.COM frag_size = hdr.frag_length; 3355772Sas200622 336*10475Samw@Sun.COM if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) { 337*10475Samw@Sun.COM nds_show_state(nds); 338*10475Samw@Sun.COM return (-1); 339*10475Samw@Sun.COM } 3405772Sas200622 341*10475Samw@Sun.COM ndr_clnt_remove_hdr(nds); 342*10475Samw@Sun.COM nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE; 3435772Sas200622 } while (!last_frag); 3445772Sas200622 3455772Sas200622 return (0); 3465772Sas200622 } 347*10475Samw@Sun.COM 348*10475Samw@Sun.COM /* 349*10475Samw@Sun.COM * Read the next RPC fragment. The xa_read() calls correspond to SmbReadX 350*10475Samw@Sun.COM * requests. Note that there is no correspondence between SmbReadX buffering 351*10475Samw@Sun.COM * and DCE RPC fragment alignment. 352*10475Samw@Sun.COM */ 353*10475Samw@Sun.COM static int 354*10475Samw@Sun.COM ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr) 355*10475Samw@Sun.COM { 356*10475Samw@Sun.COM ndr_stream_t *nds = &mxa->recv_nds; 357*10475Samw@Sun.COM unsigned long available; 358*10475Samw@Sun.COM int nbytes = 0; 359*10475Samw@Sun.COM 360*10475Samw@Sun.COM available = nds->pdu_size - nds->pdu_scan_offset; 361*10475Samw@Sun.COM 362*10475Samw@Sun.COM while (available < NDR_RSP_HDR_SIZE) { 363*10475Samw@Sun.COM if ((nbytes += (*clnt->xa_read)(clnt, mxa)) <= 0) 364*10475Samw@Sun.COM return (-1); 365*10475Samw@Sun.COM available += nbytes; 366*10475Samw@Sun.COM } 367*10475Samw@Sun.COM 368*10475Samw@Sun.COM ndr_decode_frag_hdr(nds, hdr); 369*10475Samw@Sun.COM ndr_show_hdr(hdr); 370*10475Samw@Sun.COM 371*10475Samw@Sun.COM while (available < hdr->frag_length) { 372*10475Samw@Sun.COM if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0) 373*10475Samw@Sun.COM return (-1); 374*10475Samw@Sun.COM available += nbytes; 375*10475Samw@Sun.COM } 376*10475Samw@Sun.COM 377*10475Samw@Sun.COM return (nbytes); 378*10475Samw@Sun.COM } 379