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 /* 22*9914Samw@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 345772Sas200622 358334SJose.Borrego@Sun.COM static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_t *); 368334SJose.Borrego@Sun.COM static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *); 378334SJose.Borrego@Sun.COM static void ndr_clnt_remove_hdr(ndr_stream_t *, int *); 385772Sas200622 395772Sas200622 int 408334SJose.Borrego@Sun.COM ndr_clnt_bind(ndr_client_t *clnt, const char *service_name, 418334SJose.Borrego@Sun.COM ndr_binding_t **ret_binding_p) 425772Sas200622 { 438334SJose.Borrego@Sun.COM ndr_service_t *msvc; 448334SJose.Borrego@Sun.COM ndr_binding_t *mbind; 458334SJose.Borrego@Sun.COM ndr_xa_t mxa; 467619SJose.Borrego@Sun.COM ndr_bind_hdr_t *bhdr; 478334SJose.Borrego@Sun.COM ndr_p_cont_elem_t *pce; 487619SJose.Borrego@Sun.COM ndr_bind_ack_hdr_t *bahdr; 498334SJose.Borrego@Sun.COM ndr_p_result_t *pre; 505772Sas200622 int rc; 515772Sas200622 525772Sas200622 bzero(&mxa, sizeof (mxa)); 535772Sas200622 548334SJose.Borrego@Sun.COM msvc = ndr_svc_lookup_name(service_name); 555772Sas200622 if (msvc == NULL) 568334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID); 575772Sas200622 588334SJose.Borrego@Sun.COM mxa.binding_list = clnt->binding_list; 598334SJose.Borrego@Sun.COM if ((mbind = ndr_svc_new_binding(&mxa)) == NULL) 608334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_BIND_NO_SLOTS); 615772Sas200622 628334SJose.Borrego@Sun.COM ndr_clnt_init_hdr(clnt, &mxa); 635772Sas200622 645772Sas200622 bhdr = &mxa.send_hdr.bind_hdr; 658334SJose.Borrego@Sun.COM bhdr->common_hdr.ptype = NDR_PTYPE_BIND; 665772Sas200622 bhdr->common_hdr.frag_length = sizeof (*bhdr); 678334SJose.Borrego@Sun.COM bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ; 688334SJose.Borrego@Sun.COM bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ; 695772Sas200622 bhdr->assoc_group_id = 0; 705772Sas200622 bhdr->p_context_elem.n_context_elem = 1; 715772Sas200622 725772Sas200622 /* Assign presentation context id */ 735772Sas200622 pce = &bhdr->p_context_elem.p_cont_elem[0]; 748334SJose.Borrego@Sun.COM pce->p_cont_id = clnt->next_p_cont_id++; 755772Sas200622 pce->n_transfer_syn = 1; 765772Sas200622 775772Sas200622 /* Set up UUIDs and versions from the service */ 785772Sas200622 pce->abstract_syntax.if_version = msvc->abstract_syntax_version; 798334SJose.Borrego@Sun.COM rc = ndr_uuid_parse(msvc->abstract_syntax_uuid, 805772Sas200622 &pce->abstract_syntax.if_uuid); 818334SJose.Borrego@Sun.COM if (rc != 0) 828334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID); 835772Sas200622 845772Sas200622 pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version; 858334SJose.Borrego@Sun.COM rc = ndr_uuid_parse(msvc->transfer_syntax_uuid, 865772Sas200622 &pce->transfer_syntaxes[0].if_uuid); 878334SJose.Borrego@Sun.COM if (rc != 0) 888334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID); 895772Sas200622 905772Sas200622 /* Format and exchange the PDU */ 915772Sas200622 928334SJose.Borrego@Sun.COM if ((*clnt->xa_init)(clnt, &mxa) < 0) 938334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_OUT_OF_MEMORY); 945772Sas200622 958334SJose.Borrego@Sun.COM rc = ndr_encode_pdu_hdr(&mxa); 968334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 975772Sas200622 goto fault_exit; 985772Sas200622 998334SJose.Borrego@Sun.COM if ((*clnt->xa_exchange)(clnt, &mxa) < 0) { 1008334SJose.Borrego@Sun.COM rc = NDR_DRC_FAULT_SEND_FAILED; 1015772Sas200622 goto fault_exit; 1028334SJose.Borrego@Sun.COM } 1035772Sas200622 1048334SJose.Borrego@Sun.COM rc = ndr_decode_pdu_hdr(&mxa); 1058334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1065772Sas200622 goto fault_exit; 1075772Sas200622 1085772Sas200622 /* done with buffers */ 1098334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa); 1105772Sas200622 1115772Sas200622 bahdr = &mxa.recv_hdr.bind_ack_hdr; 1125772Sas200622 1138334SJose.Borrego@Sun.COM if (mxa.ptype != NDR_PTYPE_BIND_ACK) 1148334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 1155772Sas200622 1165772Sas200622 if (bahdr->p_result_list.n_results != 1) 1178334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 1185772Sas200622 1195772Sas200622 pre = &bahdr->p_result_list.p_results[0]; 1205772Sas200622 1218334SJose.Borrego@Sun.COM if (pre->result != NDR_PCDR_ACCEPTANCE) 1228334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 1235772Sas200622 1245772Sas200622 mbind->p_cont_id = pce->p_cont_id; 1258334SJose.Borrego@Sun.COM mbind->which_side = NDR_BIND_SIDE_CLIENT; 1268334SJose.Borrego@Sun.COM mbind->clnt = clnt; 1275772Sas200622 mbind->service = msvc; 1285772Sas200622 mbind->instance_specific = 0; 1295772Sas200622 1305772Sas200622 *ret_binding_p = mbind; 1318334SJose.Borrego@Sun.COM return (NDR_DRC_OK); 1325772Sas200622 1335772Sas200622 fault_exit: 1348334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa); 1355772Sas200622 return (rc); 1365772Sas200622 } 1375772Sas200622 1385772Sas200622 int 1398334SJose.Borrego@Sun.COM ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params) 1405772Sas200622 { 1418334SJose.Borrego@Sun.COM ndr_client_t *clnt = mbind->clnt; 1428334SJose.Borrego@Sun.COM ndr_service_t *msvc = mbind->service; 1438334SJose.Borrego@Sun.COM ndr_xa_t mxa; 1447619SJose.Borrego@Sun.COM ndr_request_hdr_t *reqhdr; 1457619SJose.Borrego@Sun.COM ndr_common_header_t *rsphdr; 1468334SJose.Borrego@Sun.COM unsigned long recv_pdu_scan_offset; 1475772Sas200622 int rc; 1485772Sas200622 149*9914Samw@Sun.COM if (ndr_svc_lookup_name(msvc->name) == NULL) 150*9914Samw@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID); 1515772Sas200622 1525772Sas200622 bzero(&mxa, sizeof (mxa)); 1538334SJose.Borrego@Sun.COM mxa.ptype = NDR_PTYPE_REQUEST; 1545772Sas200622 mxa.opnum = opnum; 1555772Sas200622 mxa.binding = mbind; 1565772Sas200622 1578334SJose.Borrego@Sun.COM ndr_clnt_init_hdr(clnt, &mxa); 1585772Sas200622 1595772Sas200622 reqhdr = &mxa.send_hdr.request_hdr; 1608334SJose.Borrego@Sun.COM reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST; 1615772Sas200622 reqhdr->p_cont_id = mbind->p_cont_id; 1625772Sas200622 reqhdr->opnum = opnum; 1635772Sas200622 1648334SJose.Borrego@Sun.COM rc = (*clnt->xa_init)(clnt, &mxa); 1658334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1665772Sas200622 return (rc); 1675772Sas200622 1685772Sas200622 /* Reserve room for hdr */ 1698334SJose.Borrego@Sun.COM mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr); 1705772Sas200622 1718334SJose.Borrego@Sun.COM rc = ndr_encode_call(&mxa, params); 1728334SJose.Borrego@Sun.COM if (!NDR_DRC_IS_OK(rc)) 1735772Sas200622 goto fault_exit; 1745772Sas200622 1758334SJose.Borrego@Sun.COM mxa.send_nds.pdu_scan_offset = 0; 1765772Sas200622 1775772Sas200622 /* 1785772Sas200622 * Now we have the PDU size, we need to set up the 1795772Sas200622 * frag_length and calculate the alloc_hint. 1805772Sas200622 */ 1818334SJose.Borrego@Sun.COM mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size; 1828334SJose.Borrego@Sun.COM reqhdr->alloc_hint = mxa.send_nds.pdu_size - 1837619SJose.Borrego@Sun.COM sizeof (ndr_request_hdr_t); 1845772Sas200622 1858334SJose.Borrego@Sun.COM rc = ndr_encode_pdu_hdr(&mxa); 1868334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1875772Sas200622 goto fault_exit; 1885772Sas200622 1898334SJose.Borrego@Sun.COM rc = (*clnt->xa_exchange)(clnt, &mxa); 1908334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1915772Sas200622 goto fault_exit; 1925772Sas200622 1938334SJose.Borrego@Sun.COM rc = ndr_decode_pdu_hdr(&mxa); 1948334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 1955772Sas200622 goto fault_exit; 1965772Sas200622 1978334SJose.Borrego@Sun.COM if (mxa.ptype != NDR_PTYPE_RESPONSE) { 1988334SJose.Borrego@Sun.COM rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 1995772Sas200622 goto fault_exit; 2005772Sas200622 } 2015772Sas200622 2025772Sas200622 rsphdr = &mxa.recv_hdr.common_hdr; 2035772Sas200622 2048334SJose.Borrego@Sun.COM if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) { 2055772Sas200622 /* 2065772Sas200622 * This is a multi-fragment response. 2075772Sas200622 * Preserve the current scan offset while getting 2085772Sas200622 * fragments so that we can continue afterward 2095772Sas200622 * as if we had received the entire response as 2105772Sas200622 * a single PDU. 2115772Sas200622 */ 2128334SJose.Borrego@Sun.COM recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset; 2135772Sas200622 2148334SJose.Borrego@Sun.COM if (ndr_clnt_get_frags(clnt, &mxa) < 0) { 2158334SJose.Borrego@Sun.COM rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 2165772Sas200622 goto fault_exit; 2175772Sas200622 } 2185772Sas200622 2198334SJose.Borrego@Sun.COM mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset; 2205772Sas200622 } 2215772Sas200622 2228334SJose.Borrego@Sun.COM rc = ndr_decode_return(&mxa, params); 2238334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) 2245772Sas200622 goto fault_exit; 2255772Sas200622 2268334SJose.Borrego@Sun.COM (*clnt->xa_preserve)(clnt, &mxa); 2278334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa); 2288334SJose.Borrego@Sun.COM return (NDR_DRC_OK); 2295772Sas200622 2305772Sas200622 fault_exit: 2318334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa); 2325772Sas200622 return (rc); 2335772Sas200622 } 2345772Sas200622 2355772Sas200622 void 2368334SJose.Borrego@Sun.COM ndr_clnt_free_heap(ndr_client_t *clnt) 2375772Sas200622 { 2388334SJose.Borrego@Sun.COM (*clnt->xa_release)(clnt); 2395772Sas200622 } 2405772Sas200622 2415772Sas200622 static void 2428334SJose.Borrego@Sun.COM ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa) 2435772Sas200622 { 2447619SJose.Borrego@Sun.COM ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; 2455772Sas200622 2465772Sas200622 hdr->rpc_vers = 5; 2475772Sas200622 hdr->rpc_vers_minor = 0; 2488334SJose.Borrego@Sun.COM hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; 2498334SJose.Borrego@Sun.COM hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII; 2505772Sas200622 #ifndef _BIG_ENDIAN 2518334SJose.Borrego@Sun.COM hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN; 2525772Sas200622 #endif 2535772Sas200622 /* hdr->frag_length */ 2545772Sas200622 hdr->auth_length = 0; 2558334SJose.Borrego@Sun.COM hdr->call_id = clnt->next_call_id++; 2565772Sas200622 } 2575772Sas200622 2585772Sas200622 /* 2598334SJose.Borrego@Sun.COM * ndr_clnt_remove_hdr 2605772Sas200622 * 2615772Sas200622 * Remove an RPC fragment header from the received data stream. 2625772Sas200622 * 2635772Sas200622 * Original RPC receive buffer: 2645772Sas200622 * |- frag1 -| |-frag M(partial)-| 2655772Sas200622 * +==================+=============+----+=================+ 2665772Sas200622 * | SmbTransact Rsp1 | SmbTransact | | SmbReadX RspN | 2675772Sas200622 * | (with RPC hdr) | Rsp2 | .. | (with RPC hdr) | 2685772Sas200622 * +-----+------------+-------------+ +-----+-----------+ 2695772Sas200622 * | hdr | data | data | .. | hdr | data | 2705772Sas200622 * +=====+============+=============+----+=====+===========+ 2715772Sas200622 * <------ 2725772Sas200622 * ^ ^ ^ 2735772Sas200622 * | | | 2745772Sas200622 * base_offset hdr data 2755772Sas200622 * 2765772Sas200622 * |-------------------------------------|-----------------| 2775772Sas200622 * offset len 2785772Sas200622 * 2795772Sas200622 * RPC receive buffer (after this call): 2805772Sas200622 * +==================+=============+----+===========+ 2815772Sas200622 * | SmbTransact Rsp1 | SmbTransact | | SmbReadX | 2825772Sas200622 * | (with RPC hdr) | Rsp2 | .. | RspN | 2835772Sas200622 * +-----+------------+-------------+ +-----------+ 2845772Sas200622 * | hdr | data | data | .. | data | 2855772Sas200622 * +=====+============+=============+----+===========+ 2865772Sas200622 */ 2875772Sas200622 static void 2888334SJose.Borrego@Sun.COM ndr_clnt_remove_hdr(ndr_stream_t *nds, int *nbytes) 2895772Sas200622 { 2905772Sas200622 char *hdr; 2915772Sas200622 char *data; 2925772Sas200622 2938334SJose.Borrego@Sun.COM hdr = (char *)nds->pdu_base_offset + nds->pdu_scan_offset; 2948334SJose.Borrego@Sun.COM data = hdr + NDR_RSP_HDR_SIZE; 2958334SJose.Borrego@Sun.COM *nbytes -= NDR_RSP_HDR_SIZE; 2965772Sas200622 2975772Sas200622 bcopy(data, hdr, *nbytes); 2988334SJose.Borrego@Sun.COM nds->pdu_size -= NDR_RSP_HDR_SIZE; 2995772Sas200622 } 3005772Sas200622 3015772Sas200622 /* 3028334SJose.Borrego@Sun.COM * ndr_clnt_get_frags 3035772Sas200622 * 3045772Sas200622 * A DCE RPC message that is larger than a single fragment is transmitted 3055772Sas200622 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for 3065772Sas200622 * both Windows 2000 and 2003. 3075772Sas200622 * 3085772Sas200622 * Collect RPC fragments and append them to the receive stream buffer. 3095772Sas200622 * Each received fragment has a header, which we need to remove as we 3105772Sas200622 * build the full RPC PDU. 3115772Sas200622 * 3125772Sas200622 * The xa_read() calls will translate to SmbReadX requests. Note that 3135772Sas200622 * there is no correspondence between SmbReadX buffering and DCE RPC 3145772Sas200622 * fragment alignment. 3155772Sas200622 * 3165772Sas200622 * Return -1 on error. Otherwise, return the total data count of the 3175772Sas200622 * complete RPC response upon success. 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_rcvd; 3255772Sas200622 int frag_size; 3265772Sas200622 int last_frag; 3275772Sas200622 int nbytes; 3285772Sas200622 3295772Sas200622 /* 3305772Sas200622 * The scan offest will be used to locate the frag header. 3315772Sas200622 */ 3328334SJose.Borrego@Sun.COM nds->pdu_scan_offset = nds->pdu_base_offset + nds->pdu_size; 3335772Sas200622 3345772Sas200622 do { 3355772Sas200622 frag_rcvd = 0; 3365772Sas200622 3375772Sas200622 do { 3388334SJose.Borrego@Sun.COM if ((nbytes = (*clnt->xa_read)(clnt, mxa)) < 0) 3395772Sas200622 return (-1); 3405772Sas200622 3415772Sas200622 if (frag_rcvd == 0) { 3428334SJose.Borrego@Sun.COM ndr_decode_frag_hdr(nds, &hdr); 3435772Sas200622 3448334SJose.Borrego@Sun.COM last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags); 3458334SJose.Borrego@Sun.COM frag_size = hdr.frag_length - NDR_RSP_HDR_SIZE; 3465772Sas200622 3478334SJose.Borrego@Sun.COM ndr_clnt_remove_hdr(nds, &nbytes); 3488334SJose.Borrego@Sun.COM nds->pdu_scan_offset += frag_size; 3495772Sas200622 } 3505772Sas200622 3515772Sas200622 frag_rcvd += nbytes; 3525772Sas200622 3535772Sas200622 } while (frag_rcvd < frag_size); 3545772Sas200622 } while (!last_frag); 3555772Sas200622 3565772Sas200622 return (0); 3575772Sas200622 } 358