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*11963SAfshin.Ardakani@Sun.COM * Copyright 2010 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
3410475Samw@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 *);
378334SJose.Borrego@Sun.COM static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *);
3810475Samw@Sun.COM static int ndr_clnt_get_frag(ndr_client_t *, ndr_xa_t *, ndr_common_header_t *);
395772Sas200622
405772Sas200622 int
ndr_clnt_bind(ndr_client_t * clnt,const char * service_name,ndr_binding_t ** ret_binding_p)418334SJose.Borrego@Sun.COM ndr_clnt_bind(ndr_client_t *clnt, const char *service_name,
428334SJose.Borrego@Sun.COM ndr_binding_t **ret_binding_p)
435772Sas200622 {
448334SJose.Borrego@Sun.COM ndr_service_t *msvc;
458334SJose.Borrego@Sun.COM ndr_binding_t *mbind;
468334SJose.Borrego@Sun.COM ndr_xa_t mxa;
477619SJose.Borrego@Sun.COM ndr_bind_hdr_t *bhdr;
488334SJose.Borrego@Sun.COM ndr_p_cont_elem_t *pce;
497619SJose.Borrego@Sun.COM ndr_bind_ack_hdr_t *bahdr;
508334SJose.Borrego@Sun.COM ndr_p_result_t *pre;
515772Sas200622 int rc;
525772Sas200622
535772Sas200622 bzero(&mxa, sizeof (mxa));
545772Sas200622
558334SJose.Borrego@Sun.COM msvc = ndr_svc_lookup_name(service_name);
565772Sas200622 if (msvc == NULL)
578334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID);
585772Sas200622
598334SJose.Borrego@Sun.COM mxa.binding_list = clnt->binding_list;
608334SJose.Borrego@Sun.COM if ((mbind = ndr_svc_new_binding(&mxa)) == NULL)
618334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_BIND_NO_SLOTS);
625772Sas200622
638334SJose.Borrego@Sun.COM ndr_clnt_init_hdr(clnt, &mxa);
645772Sas200622
655772Sas200622 bhdr = &mxa.send_hdr.bind_hdr;
668334SJose.Borrego@Sun.COM bhdr->common_hdr.ptype = NDR_PTYPE_BIND;
675772Sas200622 bhdr->common_hdr.frag_length = sizeof (*bhdr);
688334SJose.Borrego@Sun.COM bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ;
698334SJose.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];
758334SJose.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;
808334SJose.Borrego@Sun.COM rc = ndr_uuid_parse(msvc->abstract_syntax_uuid,
815772Sas200622 &pce->abstract_syntax.if_uuid);
828334SJose.Borrego@Sun.COM if (rc != 0)
838334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID);
845772Sas200622
855772Sas200622 pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version;
868334SJose.Borrego@Sun.COM rc = ndr_uuid_parse(msvc->transfer_syntax_uuid,
875772Sas200622 &pce->transfer_syntaxes[0].if_uuid);
888334SJose.Borrego@Sun.COM if (rc != 0)
898334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID);
905772Sas200622
915772Sas200622 /* Format and exchange the PDU */
925772Sas200622
938334SJose.Borrego@Sun.COM if ((*clnt->xa_init)(clnt, &mxa) < 0)
948334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_OUT_OF_MEMORY);
955772Sas200622
968334SJose.Borrego@Sun.COM rc = ndr_encode_pdu_hdr(&mxa);
978334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc))
985772Sas200622 goto fault_exit;
995772Sas200622
1008334SJose.Borrego@Sun.COM if ((*clnt->xa_exchange)(clnt, &mxa) < 0) {
1018334SJose.Borrego@Sun.COM rc = NDR_DRC_FAULT_SEND_FAILED;
1025772Sas200622 goto fault_exit;
1038334SJose.Borrego@Sun.COM }
1045772Sas200622
1058334SJose.Borrego@Sun.COM rc = ndr_decode_pdu_hdr(&mxa);
1068334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc))
1075772Sas200622 goto fault_exit;
1085772Sas200622
1095772Sas200622 /* done with buffers */
1108334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa);
1115772Sas200622
1125772Sas200622 bahdr = &mxa.recv_hdr.bind_ack_hdr;
1135772Sas200622
1148334SJose.Borrego@Sun.COM if (mxa.ptype != NDR_PTYPE_BIND_ACK)
1158334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
1165772Sas200622
1175772Sas200622 if (bahdr->p_result_list.n_results != 1)
1188334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
1195772Sas200622
1205772Sas200622 pre = &bahdr->p_result_list.p_results[0];
1215772Sas200622
1228334SJose.Borrego@Sun.COM if (pre->result != NDR_PCDR_ACCEPTANCE)
1238334SJose.Borrego@Sun.COM return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
1245772Sas200622
1255772Sas200622 mbind->p_cont_id = pce->p_cont_id;
1268334SJose.Borrego@Sun.COM mbind->which_side = NDR_BIND_SIDE_CLIENT;
1278334SJose.Borrego@Sun.COM mbind->clnt = clnt;
1285772Sas200622 mbind->service = msvc;
1295772Sas200622 mbind->instance_specific = 0;
1305772Sas200622
1315772Sas200622 *ret_binding_p = mbind;
1328334SJose.Borrego@Sun.COM return (NDR_DRC_OK);
1335772Sas200622
1345772Sas200622 fault_exit:
1358334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa);
1365772Sas200622 return (rc);
1375772Sas200622 }
1385772Sas200622
1395772Sas200622 int
ndr_clnt_call(ndr_binding_t * mbind,int opnum,void * params)1408334SJose.Borrego@Sun.COM ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params)
1415772Sas200622 {
1428334SJose.Borrego@Sun.COM ndr_client_t *clnt = mbind->clnt;
1438334SJose.Borrego@Sun.COM ndr_service_t *msvc = mbind->service;
1448334SJose.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;
1478334SJose.Borrego@Sun.COM unsigned long recv_pdu_scan_offset;
1485772Sas200622 int rc;
1495772Sas200622
1509914Samw@Sun.COM if (ndr_svc_lookup_name(msvc->name) == NULL)
1519914Samw@Sun.COM return (NDR_DRC_FAULT_API_SERVICE_INVALID);
1525772Sas200622
1535772Sas200622 bzero(&mxa, sizeof (mxa));
1548334SJose.Borrego@Sun.COM mxa.ptype = NDR_PTYPE_REQUEST;
1555772Sas200622 mxa.opnum = opnum;
1565772Sas200622 mxa.binding = mbind;
1575772Sas200622
1588334SJose.Borrego@Sun.COM ndr_clnt_init_hdr(clnt, &mxa);
1595772Sas200622
1605772Sas200622 reqhdr = &mxa.send_hdr.request_hdr;
1618334SJose.Borrego@Sun.COM reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST;
1625772Sas200622 reqhdr->p_cont_id = mbind->p_cont_id;
1635772Sas200622 reqhdr->opnum = opnum;
1645772Sas200622
1658334SJose.Borrego@Sun.COM rc = (*clnt->xa_init)(clnt, &mxa);
1668334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc))
1675772Sas200622 return (rc);
1685772Sas200622
1695772Sas200622 /* Reserve room for hdr */
1708334SJose.Borrego@Sun.COM mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr);
1715772Sas200622
1728334SJose.Borrego@Sun.COM rc = ndr_encode_call(&mxa, params);
1738334SJose.Borrego@Sun.COM if (!NDR_DRC_IS_OK(rc))
1745772Sas200622 goto fault_exit;
1755772Sas200622
1768334SJose.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 */
1828334SJose.Borrego@Sun.COM mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size;
1838334SJose.Borrego@Sun.COM reqhdr->alloc_hint = mxa.send_nds.pdu_size -
1847619SJose.Borrego@Sun.COM sizeof (ndr_request_hdr_t);
1855772Sas200622
1868334SJose.Borrego@Sun.COM rc = ndr_encode_pdu_hdr(&mxa);
1878334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc))
1885772Sas200622 goto fault_exit;
1895772Sas200622
1908334SJose.Borrego@Sun.COM rc = (*clnt->xa_exchange)(clnt, &mxa);
1918334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc))
1925772Sas200622 goto fault_exit;
1935772Sas200622
1948334SJose.Borrego@Sun.COM rc = ndr_decode_pdu_hdr(&mxa);
1958334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc))
1965772Sas200622 goto fault_exit;
1975772Sas200622
1988334SJose.Borrego@Sun.COM if (mxa.ptype != NDR_PTYPE_RESPONSE) {
1998334SJose.Borrego@Sun.COM rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
2005772Sas200622 goto fault_exit;
2015772Sas200622 }
2025772Sas200622
2035772Sas200622 rsphdr = &mxa.recv_hdr.common_hdr;
2045772Sas200622
2058334SJose.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 */
21310475Samw@Sun.COM (void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL);
21410475Samw@Sun.COM
2158334SJose.Borrego@Sun.COM recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset;
21610475Samw@Sun.COM mxa.recv_nds.pdu_scan_offset = rsphdr->frag_length;
21710475Samw@Sun.COM mxa.recv_nds.pdu_size = rsphdr->frag_length;
2185772Sas200622
2198334SJose.Borrego@Sun.COM if (ndr_clnt_get_frags(clnt, &mxa) < 0) {
2208334SJose.Borrego@Sun.COM rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
2215772Sas200622 goto fault_exit;
2225772Sas200622 }
2235772Sas200622
2248334SJose.Borrego@Sun.COM mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset;
2255772Sas200622 }
2265772Sas200622
2278334SJose.Borrego@Sun.COM rc = ndr_decode_return(&mxa, params);
2288334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc))
2295772Sas200622 goto fault_exit;
2305772Sas200622
2318334SJose.Borrego@Sun.COM (*clnt->xa_preserve)(clnt, &mxa);
2328334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa);
2338334SJose.Borrego@Sun.COM return (NDR_DRC_OK);
2345772Sas200622
2355772Sas200622 fault_exit:
2368334SJose.Borrego@Sun.COM (*clnt->xa_destruct)(clnt, &mxa);
2375772Sas200622 return (rc);
2385772Sas200622 }
2395772Sas200622
2405772Sas200622 void
ndr_clnt_free_heap(ndr_client_t * clnt)2418334SJose.Borrego@Sun.COM ndr_clnt_free_heap(ndr_client_t *clnt)
2425772Sas200622 {
2438334SJose.Borrego@Sun.COM (*clnt->xa_release)(clnt);
2445772Sas200622 }
2455772Sas200622
2465772Sas200622 static void
ndr_clnt_init_hdr(ndr_client_t * clnt,ndr_xa_t * mxa)2478334SJose.Borrego@Sun.COM ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa)
2485772Sas200622 {
2497619SJose.Borrego@Sun.COM ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
2505772Sas200622
2515772Sas200622 hdr->rpc_vers = 5;
2525772Sas200622 hdr->rpc_vers_minor = 0;
2538334SJose.Borrego@Sun.COM hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
2548334SJose.Borrego@Sun.COM hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII;
2555772Sas200622 #ifndef _BIG_ENDIAN
2568334SJose.Borrego@Sun.COM hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN;
2575772Sas200622 #endif
2585772Sas200622 /* hdr->frag_length */
2595772Sas200622 hdr->auth_length = 0;
2608334SJose.Borrego@Sun.COM hdr->call_id = clnt->next_call_id++;
2615772Sas200622 }
2625772Sas200622
2635772Sas200622 /*
2648334SJose.Borrego@Sun.COM * ndr_clnt_get_frags
2655772Sas200622 *
2665772Sas200622 * A DCE RPC message that is larger than a single fragment is transmitted
2675772Sas200622 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for
2685772Sas200622 * both Windows 2000 and 2003.
2695772Sas200622 *
2705772Sas200622 * Collect RPC fragments and append them to the receive stream buffer.
2715772Sas200622 * Each received fragment has a header, which we need to remove as we
27210475Samw@Sun.COM * build the full RPC PDU. The scan offset is used to track frag headers.
2735772Sas200622 */
2745772Sas200622 static int
ndr_clnt_get_frags(ndr_client_t * clnt,ndr_xa_t * mxa)2758334SJose.Borrego@Sun.COM ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa)
2765772Sas200622 {
2778334SJose.Borrego@Sun.COM ndr_stream_t *nds = &mxa->recv_nds;
2787619SJose.Borrego@Sun.COM ndr_common_header_t hdr;
2795772Sas200622 int frag_size;
2805772Sas200622 int last_frag;
2815772Sas200622
2825772Sas200622 do {
28310475Samw@Sun.COM if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) {
28410475Samw@Sun.COM nds_show_state(nds);
28510475Samw@Sun.COM return (-1);
28610475Samw@Sun.COM }
2875772Sas200622
28810475Samw@Sun.COM last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags);
28910475Samw@Sun.COM frag_size = hdr.frag_length;
2905772Sas200622
29110475Samw@Sun.COM if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) {
29210475Samw@Sun.COM nds_show_state(nds);
29310475Samw@Sun.COM return (-1);
29410475Samw@Sun.COM }
2955772Sas200622
296*11963SAfshin.Ardakani@Sun.COM ndr_remove_frag_hdr(nds);
29710475Samw@Sun.COM nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE;
2985772Sas200622 } while (!last_frag);
2995772Sas200622
3005772Sas200622 return (0);
3015772Sas200622 }
30210475Samw@Sun.COM
30310475Samw@Sun.COM /*
30410475Samw@Sun.COM * Read the next RPC fragment. The xa_read() calls correspond to SmbReadX
30510475Samw@Sun.COM * requests. Note that there is no correspondence between SmbReadX buffering
30610475Samw@Sun.COM * and DCE RPC fragment alignment.
30710475Samw@Sun.COM */
30810475Samw@Sun.COM static int
ndr_clnt_get_frag(ndr_client_t * clnt,ndr_xa_t * mxa,ndr_common_header_t * hdr)30910475Samw@Sun.COM ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr)
31010475Samw@Sun.COM {
31110475Samw@Sun.COM ndr_stream_t *nds = &mxa->recv_nds;
31210475Samw@Sun.COM unsigned long available;
31310475Samw@Sun.COM int nbytes = 0;
31410475Samw@Sun.COM
31510475Samw@Sun.COM available = nds->pdu_size - nds->pdu_scan_offset;
31610475Samw@Sun.COM
31710475Samw@Sun.COM while (available < NDR_RSP_HDR_SIZE) {
31810475Samw@Sun.COM if ((nbytes += (*clnt->xa_read)(clnt, mxa)) <= 0)
31910475Samw@Sun.COM return (-1);
32010475Samw@Sun.COM available += nbytes;
32110475Samw@Sun.COM }
32210475Samw@Sun.COM
32310475Samw@Sun.COM ndr_decode_frag_hdr(nds, hdr);
32410475Samw@Sun.COM ndr_show_hdr(hdr);
32510475Samw@Sun.COM
32610475Samw@Sun.COM while (available < hdr->frag_length) {
32710475Samw@Sun.COM if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0)
32810475Samw@Sun.COM return (-1);
32910475Samw@Sun.COM available += nbytes;
33010475Samw@Sun.COM }
33110475Samw@Sun.COM
33210475Samw@Sun.COM return (nbytes);
33310475Samw@Sun.COM }
334