Home | History | Annotate | Download | only in common
      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 2009 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 void ndr_clnt_remove_hdr(ndr_stream_t *);
     38 static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *);
     39 static int ndr_clnt_get_frag(ndr_client_t *, ndr_xa_t *, ndr_common_header_t *);
     40 
     41 int
     42 ndr_clnt_bind(ndr_client_t *clnt, const char *service_name,
     43     ndr_binding_t **ret_binding_p)
     44 {
     45 	ndr_service_t		*msvc;
     46 	ndr_binding_t		*mbind;
     47 	ndr_xa_t		mxa;
     48 	ndr_bind_hdr_t		*bhdr;
     49 	ndr_p_cont_elem_t 	*pce;
     50 	ndr_bind_ack_hdr_t	*bahdr;
     51 	ndr_p_result_t		*pre;
     52 	int			rc;
     53 
     54 	bzero(&mxa, sizeof (mxa));
     55 
     56 	msvc = ndr_svc_lookup_name(service_name);
     57 	if (msvc == NULL)
     58 		return (NDR_DRC_FAULT_API_SERVICE_INVALID);
     59 
     60 	mxa.binding_list = clnt->binding_list;
     61 	if ((mbind = ndr_svc_new_binding(&mxa)) == NULL)
     62 		return (NDR_DRC_FAULT_API_BIND_NO_SLOTS);
     63 
     64 	ndr_clnt_init_hdr(clnt, &mxa);
     65 
     66 	bhdr = &mxa.send_hdr.bind_hdr;
     67 	bhdr->common_hdr.ptype = NDR_PTYPE_BIND;
     68 	bhdr->common_hdr.frag_length = sizeof (*bhdr);
     69 	bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ;
     70 	bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ;
     71 	bhdr->assoc_group_id = 0;
     72 	bhdr->p_context_elem.n_context_elem = 1;
     73 
     74 	/* Assign presentation context id */
     75 	pce = &bhdr->p_context_elem.p_cont_elem[0];
     76 	pce->p_cont_id = clnt->next_p_cont_id++;
     77 	pce->n_transfer_syn = 1;
     78 
     79 	/* Set up UUIDs and versions from the service */
     80 	pce->abstract_syntax.if_version = msvc->abstract_syntax_version;
     81 	rc = ndr_uuid_parse(msvc->abstract_syntax_uuid,
     82 	    &pce->abstract_syntax.if_uuid);
     83 	if (rc != 0)
     84 		return (NDR_DRC_FAULT_API_SERVICE_INVALID);
     85 
     86 	pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version;
     87 	rc = ndr_uuid_parse(msvc->transfer_syntax_uuid,
     88 	    &pce->transfer_syntaxes[0].if_uuid);
     89 	if (rc != 0)
     90 		return (NDR_DRC_FAULT_API_SERVICE_INVALID);
     91 
     92 	/* Format and exchange the PDU */
     93 
     94 	if ((*clnt->xa_init)(clnt, &mxa) < 0)
     95 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
     96 
     97 	rc = ndr_encode_pdu_hdr(&mxa);
     98 	if (NDR_DRC_IS_FAULT(rc))
     99 		goto fault_exit;
    100 
    101 	if ((*clnt->xa_exchange)(clnt, &mxa) < 0) {
    102 		rc = NDR_DRC_FAULT_SEND_FAILED;
    103 		goto fault_exit;
    104 	}
    105 
    106 	rc = ndr_decode_pdu_hdr(&mxa);
    107 	if (NDR_DRC_IS_FAULT(rc))
    108 		goto fault_exit;
    109 
    110 	/* done with buffers */
    111 	(*clnt->xa_destruct)(clnt, &mxa);
    112 
    113 	bahdr = &mxa.recv_hdr.bind_ack_hdr;
    114 
    115 	if (mxa.ptype != NDR_PTYPE_BIND_ACK)
    116 		return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
    117 
    118 	if (bahdr->p_result_list.n_results != 1)
    119 		return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
    120 
    121 	pre = &bahdr->p_result_list.p_results[0];
    122 
    123 	if (pre->result != NDR_PCDR_ACCEPTANCE)
    124 		return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
    125 
    126 	mbind->p_cont_id = pce->p_cont_id;
    127 	mbind->which_side = NDR_BIND_SIDE_CLIENT;
    128 	mbind->clnt = clnt;
    129 	mbind->service = msvc;
    130 	mbind->instance_specific = 0;
    131 
    132 	*ret_binding_p = mbind;
    133 	return (NDR_DRC_OK);
    134 
    135 fault_exit:
    136 	(*clnt->xa_destruct)(clnt, &mxa);
    137 	return (rc);
    138 }
    139 
    140 int
    141 ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params)
    142 {
    143 	ndr_client_t		*clnt = mbind->clnt;
    144 	ndr_service_t		*msvc = mbind->service;
    145 	ndr_xa_t		mxa;
    146 	ndr_request_hdr_t	*reqhdr;
    147 	ndr_common_header_t	*rsphdr;
    148 	unsigned long		recv_pdu_scan_offset;
    149 	int			rc;
    150 
    151 	if (ndr_svc_lookup_name(msvc->name) == NULL)
    152 		return (NDR_DRC_FAULT_API_SERVICE_INVALID);
    153 
    154 	bzero(&mxa, sizeof (mxa));
    155 	mxa.ptype = NDR_PTYPE_REQUEST;
    156 	mxa.opnum = opnum;
    157 	mxa.binding = mbind;
    158 
    159 	ndr_clnt_init_hdr(clnt, &mxa);
    160 
    161 	reqhdr = &mxa.send_hdr.request_hdr;
    162 	reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST;
    163 	reqhdr->p_cont_id = mbind->p_cont_id;
    164 	reqhdr->opnum = opnum;
    165 
    166 	rc = (*clnt->xa_init)(clnt, &mxa);
    167 	if (NDR_DRC_IS_FAULT(rc))
    168 		return (rc);
    169 
    170 	/* Reserve room for hdr */
    171 	mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr);
    172 
    173 	rc = ndr_encode_call(&mxa, params);
    174 	if (!NDR_DRC_IS_OK(rc))
    175 		goto fault_exit;
    176 
    177 	mxa.send_nds.pdu_scan_offset = 0;
    178 
    179 	/*
    180 	 * Now we have the PDU size, we need to set up the
    181 	 * frag_length and calculate the alloc_hint.
    182 	 */
    183 	mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size;
    184 	reqhdr->alloc_hint = mxa.send_nds.pdu_size -
    185 	    sizeof (ndr_request_hdr_t);
    186 
    187 	rc = ndr_encode_pdu_hdr(&mxa);
    188 	if (NDR_DRC_IS_FAULT(rc))
    189 		goto fault_exit;
    190 
    191 	rc = (*clnt->xa_exchange)(clnt, &mxa);
    192 	if (NDR_DRC_IS_FAULT(rc))
    193 		goto fault_exit;
    194 
    195 	rc = ndr_decode_pdu_hdr(&mxa);
    196 	if (NDR_DRC_IS_FAULT(rc))
    197 		goto fault_exit;
    198 
    199 	if (mxa.ptype != NDR_PTYPE_RESPONSE) {
    200 		rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
    201 		goto fault_exit;
    202 	}
    203 
    204 	rsphdr = &mxa.recv_hdr.common_hdr;
    205 
    206 	if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) {
    207 		/*
    208 		 * This is a multi-fragment response.
    209 		 * Preserve the current scan offset while getting
    210 		 * fragments so that we can continue afterward
    211 		 * as if we had received the entire response as
    212 		 * a single PDU.
    213 		 */
    214 		(void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL);
    215 
    216 		recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset;
    217 		mxa.recv_nds.pdu_scan_offset = rsphdr->frag_length;
    218 		mxa.recv_nds.pdu_size = rsphdr->frag_length;
    219 
    220 		if (ndr_clnt_get_frags(clnt, &mxa) < 0) {
    221 			rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
    222 			goto fault_exit;
    223 		}
    224 
    225 		mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset;
    226 	}
    227 
    228 	rc = ndr_decode_return(&mxa, params);
    229 	if (NDR_DRC_IS_FAULT(rc))
    230 		goto fault_exit;
    231 
    232 	(*clnt->xa_preserve)(clnt, &mxa);
    233 	(*clnt->xa_destruct)(clnt, &mxa);
    234 	return (NDR_DRC_OK);
    235 
    236 fault_exit:
    237 	(*clnt->xa_destruct)(clnt, &mxa);
    238 	return (rc);
    239 }
    240 
    241 void
    242 ndr_clnt_free_heap(ndr_client_t *clnt)
    243 {
    244 	(*clnt->xa_release)(clnt);
    245 }
    246 
    247 static void
    248 ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa)
    249 {
    250 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
    251 
    252 	hdr->rpc_vers = 5;
    253 	hdr->rpc_vers_minor = 0;
    254 	hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
    255 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII;
    256 #ifndef _BIG_ENDIAN
    257 	hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN;
    258 #endif
    259 	/* hdr->frag_length */
    260 	hdr->auth_length = 0;
    261 	hdr->call_id = clnt->next_call_id++;
    262 }
    263 
    264 /*
    265  * ndr_clnt_remove_hdr
    266  *
    267  * Remove an RPC fragment header from the received data stream.
    268  *
    269  * Original RPC receive buffer:
    270  * |-      frag1                   -|    |-frag M(partial)-|
    271  * +==================+=============+----+=================+
    272  * | SmbTransact Rsp1 | SmbTransact |    | SmbReadX RspN   |
    273  * | (with RPC hdr)   | Rsp2        | .. | (with RPC hdr)  |
    274  * +-----+------------+-------------+    +-----+-----------+
    275  * | hdr | data       | data        | .. | hdr | data      |
    276  * +=====+============+=============+----+=====+===========+
    277  *                                       <------
    278  * ^                                     ^     ^
    279  * |                                     |     |
    280  * base_offset                          hdr   data
    281  *
    282  * |-------------------------------------|-----------------|
    283  *            scan_offset                         len
    284  *
    285  * RPC receive buffer (after this call):
    286  * +==================+=============+----+===========+
    287  * | SmbTransact Rsp1 | SmbTransact |    | SmbReadX  |
    288  * | (with RPC hdr)   | Rsp2        | .. | RspN      |
    289  * +-----+------------+-------------+    +-----------+
    290  * | hdr | data       | data        | .. | data      |
    291  * +=====+============+=============+----+===========+
    292  */
    293 static void
    294 ndr_clnt_remove_hdr(ndr_stream_t *nds)
    295 {
    296 	char *hdr;
    297 	char *data;
    298 	int nbytes;
    299 
    300 	hdr = (char *)nds->pdu_base_offset + nds->pdu_scan_offset;
    301 	data = hdr + NDR_RSP_HDR_SIZE;
    302 	nbytes = nds->pdu_size - nds->pdu_scan_offset - NDR_RSP_HDR_SIZE;
    303 
    304 	bcopy(data, hdr, nbytes);
    305 	nds->pdu_size -= NDR_RSP_HDR_SIZE;
    306 }
    307 
    308 /*
    309  * ndr_clnt_get_frags
    310  *
    311  * A DCE RPC message that is larger than a single fragment is transmitted
    312  * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for
    313  * both Windows 2000 and 2003.
    314  *
    315  * Collect RPC fragments and append them to the receive stream buffer.
    316  * Each received fragment has a header, which we need to remove as we
    317  * build the full RPC PDU.  The scan offset is used to track frag headers.
    318  */
    319 static int
    320 ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa)
    321 {
    322 	ndr_stream_t *nds = &mxa->recv_nds;
    323 	ndr_common_header_t hdr;
    324 	int frag_size;
    325 	int last_frag;
    326 
    327 	do {
    328 		if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) {
    329 			nds_show_state(nds);
    330 			return (-1);
    331 		}
    332 
    333 		last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags);
    334 		frag_size = hdr.frag_length;
    335 
    336 		if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) {
    337 			nds_show_state(nds);
    338 			return (-1);
    339 		}
    340 
    341 		ndr_clnt_remove_hdr(nds);
    342 		nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE;
    343 	} while (!last_frag);
    344 
    345 	return (0);
    346 }
    347 
    348 /*
    349  * Read the next RPC fragment.  The xa_read() calls correspond to SmbReadX
    350  * requests.  Note that there is no correspondence between SmbReadX buffering
    351  * and DCE RPC fragment alignment.
    352  */
    353 static int
    354 ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr)
    355 {
    356 	ndr_stream_t		*nds = &mxa->recv_nds;
    357 	unsigned long		available;
    358 	int			nbytes = 0;
    359 
    360 	available = nds->pdu_size - nds->pdu_scan_offset;
    361 
    362 	while (available < NDR_RSP_HDR_SIZE) {
    363 		if ((nbytes += (*clnt->xa_read)(clnt, mxa)) <= 0)
    364 			return (-1);
    365 		available += nbytes;
    366 	}
    367 
    368 	ndr_decode_frag_hdr(nds, hdr);
    369 	ndr_show_hdr(hdr);
    370 
    371 	while (available < hdr->frag_length) {
    372 		if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0)
    373 			return (-1);
    374 		available += nbytes;
    375 	}
    376 
    377 	return (nbytes);
    378 }
    379