xref: /illumos-gate/usr/src/lib/libmlrpc/common/ndr_client.c (revision 6f1fa39e3cf1b335f342bbca41590e9d76ab29b7)
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 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
26  */
27 
28 #include <sys/errno.h>
29 #include <string.h>
30 #include <strings.h>
31 
32 #include <libmlrpc.h>
33 
34 #define	NDR_DEFAULT_FRAGSZ	8192
35 #define	NDR_MULTI_FRAGSZ	(60 * 1024)
36 
37 static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_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, ndr_service_t *msvc,
43     ndr_binding_t **ret_binding_p)
44 {
45 	ndr_binding_t		*mbind;
46 	ndr_xa_t		mxa;
47 	ndr_bind_hdr_t		*bhdr;
48 	ndr_common_header_t	*hdr;
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 	mxa.binding_list = clnt->binding_list;
57 	if ((mbind = ndr_svc_new_binding(&mxa)) == NULL)
58 		return (NDR_DRC_FAULT_API_BIND_NO_SLOTS);
59 
60 	ndr_clnt_init_hdr(clnt, &mxa);
61 
62 	bhdr = &mxa.send_hdr.bind_hdr;
63 	hdr = &mxa.send_hdr.bind_hdr.common_hdr;
64 	hdr->ptype = NDR_PTYPE_BIND;
65 	bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ;
66 	bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ;
67 	bhdr->assoc_group_id = 0;
68 	bhdr->p_context_elem.n_context_elem = 1;
69 
70 	/* Assign presentation context id */
71 	pce = &bhdr->p_context_elem.p_cont_elem[0];
72 	pce->p_cont_id = clnt->next_p_cont_id++;
73 	pce->n_transfer_syn = 1;
74 
75 	/* Set up UUIDs and versions from the service */
76 	pce->abstract_syntax.if_version = msvc->abstract_syntax_version;
77 	rc = ndr_uuid_parse(msvc->abstract_syntax_uuid,
78 	    &pce->abstract_syntax.if_uuid);
79 	if (rc != 0)
80 		return (NDR_DRC_FAULT_API_SERVICE_INVALID);
81 
82 	pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version;
83 	rc = ndr_uuid_parse(msvc->transfer_syntax_uuid,
84 	    &pce->transfer_syntaxes[0].if_uuid);
85 	if (rc != 0)
86 		return (NDR_DRC_FAULT_API_SERVICE_INVALID);
87 
88 	/* Format and exchange the PDU */
89 
90 	if ((*clnt->xa_init)(clnt, &mxa) < 0)
91 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
92 
93 	/* Reserve room for hdr */
94 	mxa.send_nds.pdu_scan_offset = sizeof (*bhdr);
95 
96 	/* GSS_Init_sec_context */
97 	rc = ndr_add_sec_context(&clnt->auth_ctx, &mxa);
98 	if (NDR_DRC_IS_FAULT(rc))
99 		goto fault_exit;
100 
101 	rc = ndr_encode_pdu_auth(&mxa);
102 	if (NDR_DRC_IS_FAULT(rc))
103 		goto fault_exit;
104 
105 	/*
106 	 * If we have auth data, then pdu_size has been initialized.
107 	 * Otherwise, it hasn't.
108 	 */
109 	if (hdr->auth_length == 0)
110 		hdr->frag_length = sizeof (*bhdr);
111 	else
112 		hdr->frag_length = mxa.send_nds.pdu_size;
113 
114 	/* Reset scan_offset to header */
115 	mxa.send_nds.pdu_scan_offset = 0;
116 
117 	rc = ndr_encode_pdu_hdr(&mxa);
118 	if (NDR_DRC_IS_FAULT(rc))
119 		goto fault_exit;
120 
121 	if ((*clnt->xa_exchange)(clnt, &mxa) < 0) {
122 		rc = NDR_DRC_FAULT_SEND_FAILED;
123 		goto fault_exit;
124 	}
125 
126 	rc = ndr_decode_pdu_hdr(&mxa);
127 	if (NDR_DRC_IS_FAULT(rc))
128 		goto fault_exit;
129 
130 	rc = ndr_decode_pdu_auth(&mxa);
131 	if (NDR_DRC_IS_FAULT(rc))
132 		goto fault_exit;
133 
134 	bahdr = &mxa.recv_hdr.bind_ack_hdr;
135 
136 	if (mxa.ptype != NDR_PTYPE_BIND_ACK) {
137 		rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
138 		goto fault_exit;
139 	}
140 
141 	if (bahdr->p_result_list.n_results != 1) {
142 		rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
143 		goto fault_exit;
144 	}
145 
146 	pre = &bahdr->p_result_list.p_results[0];
147 
148 	if (pre->result != NDR_PCDR_ACCEPTANCE) {
149 		rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
150 		goto fault_exit;
151 	}
152 
153 	/* GSS_init_sec_context 2 */
154 	rc = ndr_recv_sec_context(&clnt->auth_ctx, &mxa);
155 	if (NDR_DRC_IS_FAULT(rc))
156 		goto fault_exit;
157 
158 	/* done with buffers */
159 	(*clnt->xa_destruct)(clnt, &mxa);
160 
161 	mbind->p_cont_id = pce->p_cont_id;
162 	mbind->which_side = NDR_BIND_SIDE_CLIENT;
163 	mbind->clnt = clnt;
164 	mbind->service = msvc;
165 	mbind->instance_specific = 0;
166 
167 	*ret_binding_p = mbind;
168 	return (NDR_DRC_OK);
169 
170 fault_exit:
171 	(*clnt->xa_destruct)(clnt, &mxa);
172 	return (rc);
173 }
174 
175 int
176 ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params)
177 {
178 	ndr_client_t		*clnt = mbind->clnt;
179 	ndr_xa_t		mxa;
180 	ndr_request_hdr_t	*reqhdr;
181 	ndr_common_header_t	*rsphdr;
182 	unsigned long		recv_pdu_scan_offset, recv_pdu_size;
183 	int			rc;
184 
185 	bzero(&mxa, sizeof (mxa));
186 	mxa.ptype = NDR_PTYPE_REQUEST;
187 	mxa.opnum = opnum;
188 	mxa.binding = mbind;
189 
190 	ndr_clnt_init_hdr(clnt, &mxa);
191 
192 	reqhdr = &mxa.send_hdr.request_hdr;
193 	reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST;
194 	reqhdr->p_cont_id = mbind->p_cont_id;
195 	reqhdr->opnum = opnum;
196 
197 	rc = (*clnt->xa_init)(clnt, &mxa);
198 	if (NDR_DRC_IS_FAULT(rc))
199 		return (rc);
200 
201 	/* Reserve room for hdr */
202 	mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr);
203 	/* pdu_scan_offset now points to start of stub */
204 	mxa.send_nds.pdu_body_offset = mxa.send_nds.pdu_scan_offset;
205 
206 	rc = ndr_encode_call(&mxa, params);
207 	if (!NDR_DRC_IS_OK(rc))
208 		goto fault_exit;
209 
210 	/*
211 	 * With the Stub data encoded, calculate the alloc_hint
212 	 * before we add padding or auth data.
213 	 */
214 	reqhdr->alloc_hint = mxa.send_nds.pdu_size -
215 	    sizeof (ndr_request_hdr_t);
216 
217 	/* GSS_WrapEx/VerifyMICEx */
218 	rc = ndr_add_auth(&clnt->auth_ctx, &mxa);
219 	if (NDR_DRC_IS_FAULT(rc))
220 		goto fault_exit;
221 
222 	rc = ndr_encode_pdu_auth(&mxa);
223 	if (NDR_DRC_IS_FAULT(rc))
224 		goto fault_exit;
225 
226 	/*
227 	 * Now we have the PDU size, we need to set up the
228 	 * frag_length.
229 	 * Also reset pdu_scan_offset to header.
230 	 */
231 	mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size;
232 	mxa.send_nds.pdu_scan_offset = 0;
233 
234 	rc = ndr_encode_pdu_hdr(&mxa);
235 	if (NDR_DRC_IS_FAULT(rc))
236 		goto fault_exit;
237 
238 	rc = (*clnt->xa_exchange)(clnt, &mxa);
239 	if (NDR_DRC_IS_FAULT(rc))
240 		goto fault_exit;
241 
242 	rc = ndr_decode_pdu_hdr(&mxa);
243 	if (NDR_DRC_IS_FAULT(rc))
244 		goto fault_exit;
245 
246 	if (mxa.ptype != NDR_PTYPE_RESPONSE) {
247 		rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
248 		goto fault_exit;
249 	}
250 
251 	rc = ndr_decode_pdu_auth(&mxa);
252 	if (NDR_DRC_IS_FAULT(rc))
253 		goto fault_exit;
254 
255 	rc = ndr_check_auth(&clnt->auth_ctx, &mxa);
256 	if (NDR_DRC_IS_FAULT(rc))
257 		goto fault_exit;
258 
259 	rsphdr = &mxa.recv_hdr.common_hdr;
260 
261 	if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) {
262 		/*
263 		 * This is a multi-fragment response.
264 		 * Preserve the current body offset while getting
265 		 * fragments so that we can continue afterward
266 		 * as if we had received the entire response as
267 		 * a single PDU.
268 		 *
269 		 * GROW_PDU trashes pdu_size; reset it afterwards.
270 		 */
271 		recv_pdu_size = mxa.recv_nds.pdu_size;
272 		(void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL);
273 
274 		/*
275 		 * pdu_scan_offset needs to be the first byte after the first
276 		 * fragment in pdu_base_addr (minus the sec_trailer).
277 		 *
278 		 * pdu_size needs to be all of the (usable) data we've
279 		 * received thus far.
280 		 */
281 		recv_pdu_scan_offset = mxa.recv_nds.pdu_body_offset;
282 		mxa.recv_nds.pdu_scan_offset = mxa.recv_nds.pdu_body_offset +
283 		    mxa.recv_nds.pdu_body_size - mxa.recv_auth.auth_pad_len;
284 		mxa.recv_nds.pdu_size = recv_pdu_size;
285 
286 		rc = ndr_clnt_get_frags(clnt, &mxa);
287 		if (NDR_DRC_IS_FAULT(rc))
288 			goto fault_exit;
289 
290 		mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset;
291 	}
292 
293 	rc = ndr_decode_return(&mxa, params);
294 	if (NDR_DRC_IS_FAULT(rc))
295 		goto fault_exit;
296 
297 	(*clnt->xa_preserve)(clnt, &mxa);
298 	(*clnt->xa_destruct)(clnt, &mxa);
299 	return (NDR_DRC_OK);
300 
301 fault_exit:
302 	ndr_show_hdr(&mxa.send_hdr.common_hdr);
303 	nds_show_state(&mxa.send_nds);
304 	if (mxa.send_hdr.common_hdr.auth_length != 0)
305 		ndr_show_auth(&mxa.send_auth);
306 
307 	ndr_show_hdr(&mxa.recv_hdr.common_hdr);
308 	nds_show_state(&mxa.recv_nds);
309 	if (mxa.recv_hdr.common_hdr.auth_length != 0)
310 		ndr_show_auth(&mxa.recv_auth);
311 	(*clnt->xa_destruct)(clnt, &mxa);
312 	return (rc);
313 }
314 
315 void
316 ndr_clnt_free_heap(ndr_client_t *clnt)
317 {
318 	(*clnt->xa_release)(clnt);
319 }
320 
321 static void
322 ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa)
323 {
324 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
325 
326 	hdr->rpc_vers = 5;
327 	hdr->rpc_vers_minor = 0;
328 	hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
329 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII;
330 #ifndef _BIG_ENDIAN
331 	hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN;
332 #endif
333 	/* hdr->frag_length */
334 	hdr->auth_length = 0;
335 	hdr->call_id = clnt->next_call_id++;
336 }
337 
338 /*
339  * ndr_clnt_get_frags
340  *
341  * A DCE RPC message that is larger than a single fragment is transmitted
342  * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for
343  * both Windows 2000 and 2003.
344  *
345  * Collect RPC fragments and append them to the receive stream buffer.
346  * Each received fragment has a header, which we need to remove as we
347  * build the full RPC PDU.  The scan offset is used to track frag headers.
348  */
349 static int
350 ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa)
351 {
352 	ndr_stream_t *nds = &mxa->recv_nds;
353 	ndr_common_header_t hdr;
354 	int frag_size;
355 	int last_frag;
356 	int rc;
357 
358 	do {
359 		if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) {
360 			nds_show_state(nds);
361 			return (NDR_DRC_FAULT_RECEIVED_RUNT);
362 		}
363 
364 		last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags);
365 		frag_size = hdr.frag_length;
366 
367 		/*
368 		 * ndr_clnt_get_frag() doesn't change pdu_scan_offset.
369 		 */
370 		if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) {
371 			nds_show_state(nds);
372 			return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
373 		}
374 
375 		if (hdr.auth_length != 0 && hdr.auth_length >
376 		    (hdr.frag_length - nds->pdu_hdr_size - SEC_TRAILER_SIZE))
377 			return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
378 
379 		rc = ndr_decode_pdu_auth(mxa);
380 		if (NDR_DRC_IS_FAULT(rc))
381 			return (rc);
382 
383 		rc = ndr_check_auth(&clnt->auth_ctx, mxa);
384 		if (NDR_DRC_IS_FAULT(rc))
385 			return (rc);
386 
387 		/*
388 		 * Headers, Auth Padding, and auth data shouldn't be kept
389 		 * from fragments.
390 		 */
391 		ndr_remove_frag_hdr(nds);
392 		nds->pdu_scan_offset +=
393 		    nds->pdu_body_size - mxa->recv_auth.auth_pad_len;
394 	} while (!last_frag);
395 
396 	return (0);
397 }
398 
399 /*
400  * Read the next RPC fragment.  The xa_read() calls correspond to SmbReadX
401  * requests.  Note that there is no correspondence between SmbReadX buffering
402  * and DCE RPC fragment alignment.
403  */
404 static int
405 ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr)
406 {
407 	ndr_stream_t		*nds = &mxa->recv_nds;
408 	unsigned long		available;
409 	int			nbytes = 0;
410 
411 	available = nds->pdu_size - nds->pdu_scan_offset;
412 
413 	while (available < NDR_RSP_HDR_SIZE) {
414 		if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0)
415 			return (-1);
416 		available += nbytes;
417 	}
418 
419 	ndr_decode_frag_hdr(nds, hdr);
420 	ndr_show_hdr(hdr);
421 
422 	while (available < hdr->frag_length) {
423 		if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0)
424 			return (-1);
425 		available += nbytes;
426 	}
427 
428 	return (nbytes);
429 }
430