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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
24 */
25
26#include <assert.h>
27#include <strings.h>
28#include <sys/param.h>
29
30#include <libmlrpc.h>
31
32#ifdef _BIG_ENDIAN
33static const int ndr_native_byte_order = NDR_REPLAB_INTG_BIG_ENDIAN;
34#else
35static const int ndr_native_byte_order = NDR_REPLAB_INTG_LITTLE_ENDIAN;
36#endif
37
38static int ndr_decode_hdr_common(ndr_stream_t *, ndr_common_header_t *);
39static int ndr_decode_pac_hdr(ndr_stream_t *, ndr_pac_hdr_t *);
40
41static int
42ndr_encode_decode_common(ndr_stream_t *nds, unsigned opnum,
43    ndr_typeinfo_t *ti, void *datum)
44{
45	int rc;
46
47	/*
48	 * Perform the (un)marshalling
49	 */
50	if (ndo_operation(nds, ti, opnum, datum))
51		return (NDR_DRC_OK);
52
53	switch (nds->error) {
54	case NDR_ERR_MALLOC_FAILED:
55		rc = NDR_DRC_FAULT_OUT_OF_MEMORY;
56		break;
57
58	case NDR_ERR_SWITCH_VALUE_INVALID:
59		rc = NDR_DRC_FAULT_PARAM_0_INVALID;
60		break;
61
62	case NDR_ERR_UNDERFLOW:
63		rc = NDR_DRC_FAULT_RECEIVED_RUNT;
64		break;
65
66	case NDR_ERR_GROW_FAILED:
67		rc = NDR_DRC_FAULT_ENCODE_TOO_BIG;
68		break;
69
70	default:
71		if (nds->m_op == NDR_M_OP_MARSHALL)
72			rc = NDR_DRC_FAULT_ENCODE_FAILED;
73		else
74			rc = NDR_DRC_FAULT_DECODE_FAILED;
75		break;
76	}
77
78	return (rc);
79}
80
81ndr_buf_t *
82ndr_buf_init(ndr_typeinfo_t *ti)
83{
84	ndr_buf_t		*nbuf;
85
86	if ((nbuf = calloc(1, sizeof (ndr_buf_t))) == NULL)
87		return (NULL);
88
89	if ((nbuf->nb_heap = ndr_heap_create()) == NULL) {
90		free(nbuf);
91		return (NULL);
92	}
93
94	nbuf->nb_ti = ti;
95	nbuf->nb_magic = NDR_BUF_MAGIC;
96	return (nbuf);
97}
98
99void
100ndr_buf_fini(ndr_buf_t *nbuf)
101{
102	assert(nbuf->nb_magic == NDR_BUF_MAGIC);
103
104	nds_destruct(&nbuf->nb_nds);
105	ndr_heap_destroy(nbuf->nb_heap);
106	nbuf->nb_magic = 0;
107	free(nbuf);
108}
109
110/*
111 * Decode an NDR encoded buffer.  The buffer is expected to contain
112 * a single fragment packet with a valid PDU header followed by NDR
113 * encoded data.  The structure to which result points should be
114 * of the appropriate type to hold the decoded output.  For example:
115 *
116 *	pac_info_t info;
117 *
118 * 	if ((nbuf = ndr_buf_init(&TYPEINFO(ndr_pac)) != NULL) {
119 *		rc = ndr_decode_buf(nbuf, opnum, data, datalen, &info);
120 *		...
121 *		ndr_buf_fini(nbuf);
122 *	}
123 */
124int
125ndr_buf_decode(ndr_buf_t *nbuf, unsigned hdr_type, unsigned opnum,
126    const char *data, size_t datalen, void *result)
127{
128	ndr_common_header_t	hdr;
129	ndr_pac_hdr_t		pac_hdr;
130	unsigned		pdu_size_hint;
131	int			rc;
132
133	assert(nbuf->nb_magic == NDR_BUF_MAGIC);
134	assert(nbuf->nb_heap != NULL);
135	assert(nbuf->nb_ti != NULL);
136
137	if (datalen < NDR_PDU_SIZE_HINT_DEFAULT)
138		pdu_size_hint = NDR_PDU_SIZE_HINT_DEFAULT;
139	else
140		pdu_size_hint = datalen;
141
142	rc = nds_initialize(&nbuf->nb_nds, pdu_size_hint, NDR_MODE_BUF_DECODE,
143	    nbuf->nb_heap);
144	if (NDR_DRC_IS_FAULT(rc))
145		return (rc);
146
147	bcopy(data, nbuf->nb_nds.pdu_base_addr, datalen);
148
149	switch (hdr_type) {
150	case NDR_PTYPE_COMMON:
151		rc = ndr_decode_hdr_common(&nbuf->nb_nds, &hdr);
152		if (NDR_DRC_IS_FAULT(rc))
153			return (rc);
154
155		if (!NDR_IS_SINGLE_FRAG(hdr.pfc_flags))
156			return (NDR_DRC_FAULT_DECODE_FAILED);
157		break;
158
159	case NDR_PTYPE_PAC:
160		rc = ndr_decode_pac_hdr(&nbuf->nb_nds, &pac_hdr);
161		if (NDR_DRC_IS_FAULT(rc))
162			return (rc);
163
164		if (pac_hdr.common_hdr.hdrlen != sizeof (ndr_serialtype1_hdr_t))
165			return (NDR_DRC_FAULT_DECODE_FAILED);
166		break;
167
168	default:
169		return (NDR_ERR_UNIMPLEMENTED);
170	}
171
172	rc = ndr_encode_decode_common(&nbuf->nb_nds, opnum, nbuf->nb_ti,
173	    result);
174	return (rc);
175}
176
177/*
178 * Use the receive stream to unmarshall data (NDR_MODE_CALL_RECV).
179 */
180int
181ndr_decode_call(ndr_xa_t *mxa, void *params)
182{
183	ndr_stream_t	*nds = &mxa->recv_nds;
184	int		rc;
185
186	if (!NDR_MODE_MATCH(nds, NDR_MODE_CALL_RECV))
187		return (NDR_DRC_FAULT_MODE_MISMATCH);
188
189	rc = ndr_encode_decode_common(nds, mxa->opnum,
190	    mxa->binding->service->interface_ti, params);
191
192	return (rc + NDR_PTYPE_REQUEST);
193}
194
195/*
196 * Use the send stream to marshall data (NDR_MODE_RETURN_SEND).
197 */
198int
199ndr_encode_return(ndr_xa_t *mxa, void *params)
200{
201	ndr_stream_t	*nds = &mxa->send_nds;
202	int		rc;
203
204	if (!NDR_MODE_MATCH(nds, NDR_MODE_RETURN_SEND))
205		return (NDR_DRC_FAULT_MODE_MISMATCH);
206
207	rc = ndr_encode_decode_common(nds, mxa->opnum,
208	    mxa->binding->service->interface_ti, params);
209
210	return (rc + NDR_PTYPE_RESPONSE);
211}
212
213/*
214 * Use the send stream to marshall data (NDR_MODE_CALL_SEND).
215 */
216int
217ndr_encode_call(ndr_xa_t *mxa, void *params)
218{
219	ndr_stream_t	*nds = &mxa->send_nds;
220	int		rc;
221
222	if (!NDR_MODE_MATCH(nds, NDR_MODE_CALL_SEND))
223		return (NDR_DRC_FAULT_MODE_MISMATCH);
224
225	rc = ndr_encode_decode_common(nds, mxa->opnum,
226	    mxa->binding->service->interface_ti, params);
227
228	return (rc + NDR_PTYPE_REQUEST);
229}
230
231/*
232 * Use the receive stream to unmarshall data (NDR_MODE_RETURN_RECV).
233 */
234int
235ndr_decode_return(ndr_xa_t *mxa, void *params)
236{
237	ndr_stream_t	*nds = &mxa->recv_nds;
238	int		rc;
239
240	if (!NDR_MODE_MATCH(nds, NDR_MODE_RETURN_RECV))
241		return (NDR_DRC_FAULT_MODE_MISMATCH);
242
243	rc = ndr_encode_decode_common(nds, mxa->opnum,
244	    mxa->binding->service->interface_ti, params);
245
246	return (rc + NDR_PTYPE_RESPONSE);
247}
248
249int
250ndr_decode_pdu_hdr(ndr_xa_t *mxa)
251{
252	ndr_common_header_t	*hdr = &mxa->recv_hdr.common_hdr;
253	ndr_stream_t		*nds = &mxa->recv_nds;
254	int			rc;
255
256	rc = ndr_decode_hdr_common(nds, hdr);
257	if (NDR_DRC_IS_FAULT(rc))
258		return (rc);
259
260	/*
261	 * Verify the protocol version.
262	 */
263	if ((hdr->rpc_vers != 5) || (hdr->rpc_vers_minor != 0))
264		return (NDR_DRC_FAULT_RPCHDR_DECODE_FAILED);
265
266	mxa->ptype = hdr->ptype;
267	return (NDR_DRC_OK);
268}
269
270static int
271ndr_decode_hdr_common(ndr_stream_t *nds, ndr_common_header_t *hdr)
272{
273	int			ptype;
274	int			rc;
275	int			charset;
276	int			byte_order;
277
278	if (nds->m_op != NDR_M_OP_UNMARSHALL)
279		return (NDR_DRC_FAULT_RPCHDR_MODE_MISMATCH);
280
281	/*
282	 * All PDU headers are at least this big
283	 */
284	rc = NDS_GROW_PDU(nds, sizeof (ndr_common_header_t), 0);
285	if (!rc)
286		return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT);
287
288	/*
289	 * Peek at the first eight bytes to figure out what we're doing.
290	 */
291	rc = NDS_GET_PDU(nds, 0, 8, (char *)hdr, 0, 0);
292	if (!rc)
293		return (NDR_DRC_FAULT_RPCHDR_DECODE_FAILED);
294
295	/*
296	 * Check for ASCII as the character set.  This is an ASCII
297	 * versus EBCDIC option and has nothing to do with Unicode.
298	 */
299	charset = hdr->packed_drep.intg_char_rep & NDR_REPLAB_CHAR_MASK;
300	if (charset != NDR_REPLAB_CHAR_ASCII)
301		return (NDR_DRC_FAULT_RPCHDR_DECODE_FAILED);
302
303	/*
304	 * Set the byte swap flag if the PDU byte-order
305	 * is different from the local byte-order.
306	 */
307	byte_order = hdr->packed_drep.intg_char_rep & NDR_REPLAB_INTG_MASK;
308	nds->swap = (byte_order != ndr_native_byte_order) ? 1 : 0;
309
310	ptype = hdr->ptype;
311	if (ptype == NDR_PTYPE_REQUEST &&
312	    (hdr->pfc_flags & NDR_PFC_OBJECT_UUID) != 0) {
313		ptype = NDR_PTYPE_REQUEST_WITH;	/* fake for sizing */
314	}
315
316	rc = ndr_encode_decode_common(nds, ptype, &TYPEINFO(ndr_hdr), hdr);
317
318	return (NDR_DRC_PTYPE_RPCHDR(rc));
319}
320
321static int
322ndr_decode_pac_hdr(ndr_stream_t *nds, ndr_pac_hdr_t *hdr)
323{
324	int	rc;
325
326	if (nds->m_op != NDR_M_OP_UNMARSHALL)
327		return (NDR_DRC_FAULT_RPCHDR_MODE_MISMATCH);
328
329	/*
330	 * All PDU headers are at least this big
331	 */
332	rc = NDS_GROW_PDU(nds, sizeof (ndr_pac_hdr_t), 0);
333	if (!rc)
334		return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT);
335
336	/*
337	 * Peek at the first eight bytes to figure out what we're doing.
338	 */
339	rc = NDS_GET_PDU(nds, 0, 8, (char *)hdr, 0, 0);
340	if (!rc)
341		return (NDR_DRC_FAULT_RPCHDR_DECODE_FAILED);
342
343	/* Must be set to 1 to indicate type serialization version 1. */
344	if (hdr->common_hdr.version != 1)
345		return (NDR_DRC_FAULT_RPCHDR_DECODE_FAILED);
346
347	/*
348	 * Set the byte swap flag if the PDU byte-order
349	 * is different from the local byte-order.
350	 */
351	nds->swap =
352	    (hdr->common_hdr.endianness != ndr_native_byte_order) ? 1 : 0;
353
354	rc = ndr_encode_decode_common(nds, NDR_PTYPE_PAC,
355	    &TYPEINFO(ndr_hdr), hdr);
356
357	return (NDR_DRC_PTYPE_RPCHDR(rc));
358}
359
360/*
361 * Decode an RPC fragment header.  Use ndr_decode_pdu_hdr() to process
362 * the first fragment header then this function to process additional
363 * fragment headers.
364 */
365void
366ndr_decode_frag_hdr(ndr_stream_t *nds, ndr_common_header_t *hdr)
367{
368	ndr_common_header_t *tmp;
369	uint8_t *pdu;
370	int byte_order;
371
372	pdu = (uint8_t *)nds->pdu_base_offset + nds->pdu_scan_offset;
373	bcopy(pdu, hdr, NDR_RSP_HDR_SIZE);
374
375	/*
376	 * Swap non-byte fields if the PDU byte-order
377	 * is different from the local byte-order.
378	 */
379	byte_order = hdr->packed_drep.intg_char_rep & NDR_REPLAB_INTG_MASK;
380
381	if (byte_order != ndr_native_byte_order) {
382		/*LINTED E_BAD_PTR_CAST_ALIGN*/
383		tmp = (ndr_common_header_t *)pdu;
384
385		nds_bswap(&tmp->frag_length, &hdr->frag_length,
386		    sizeof (WORD));
387		nds_bswap(&tmp->auth_length, &hdr->auth_length,
388		    sizeof (WORD));
389		nds_bswap(&tmp->call_id, &hdr->call_id, sizeof (DWORD));
390	}
391}
392
393/*
394 * Remove an RPC fragment header from the received data stream.
395 *
396 * NDR stream on entry:
397 *
398 *                |<--- frag --->|
399 * +-----+--------+-----+--------+-----+---------+-----+
400 * | hdr |  data  | hdr |  data  | hdr |  data   | ... |
401 * +-----+--------+-----+--------+-----+---------+-----+
402 *                 <----
403 *
404 * NDR stream on return:
405 *
406 * +-----+----------------+-----+---------+-----+
407 * | hdr |       data     | hdr |  data   | ... |
408 * +-----+----------------+-----+---------+-----+
409 */
410void
411ndr_remove_frag_hdr(ndr_stream_t *nds)
412{
413	char	*hdr;
414	char	*data;
415	int	nbytes;
416
417	hdr = (char *)nds->pdu_base_offset + nds->pdu_scan_offset;
418	data = hdr + NDR_RSP_HDR_SIZE;
419	nbytes = nds->pdu_size - nds->pdu_scan_offset - NDR_RSP_HDR_SIZE;
420
421	bcopy(data, hdr, nbytes);
422	nds->pdu_size -= NDR_RSP_HDR_SIZE;
423}
424
425void
426ndr_show_hdr(ndr_common_header_t *hdr)
427{
428	char	*fragtype;
429
430	if (hdr == NULL) {
431		ndo_printf(NULL, NULL, "ndr hdr: <null>");
432		return;
433	}
434
435	if (NDR_IS_SINGLE_FRAG(hdr->pfc_flags))
436		fragtype = "single";
437	else if (NDR_IS_FIRST_FRAG(hdr->pfc_flags))
438		fragtype = "first";
439	else if (NDR_IS_LAST_FRAG(hdr->pfc_flags))
440		fragtype = "last";
441	else
442		fragtype = "intermediate";
443
444	ndo_printf(NULL, NULL,
445	    "ndr hdr: %d.%d ptype=%d, %s frag (flags=0x%08x) len=%d",
446	    hdr->rpc_vers, hdr->rpc_vers_minor, hdr->ptype,
447	    fragtype, hdr->pfc_flags, hdr->frag_length);
448}
449
450int
451ndr_encode_pdu_hdr(ndr_xa_t *mxa)
452{
453	ndr_common_header_t	*hdr = &mxa->send_hdr.common_hdr;
454	ndr_stream_t		*nds = &mxa->send_nds;
455	int			ptype;
456	int			rc;
457
458	if (nds->m_op != NDR_M_OP_MARSHALL)
459		return (NDR_DRC_FAULT_RPCHDR_MODE_MISMATCH);
460
461	ptype = hdr->ptype;
462	if (ptype == NDR_PTYPE_REQUEST &&
463	    (hdr->pfc_flags & NDR_PFC_OBJECT_UUID) != 0) {
464		ptype = NDR_PTYPE_REQUEST_WITH;	/* fake for sizing */
465	}
466
467	rc = ndr_encode_decode_common(nds, ptype, &TYPEINFO(ndr_hdr), hdr);
468
469	return (NDR_DRC_PTYPE_RPCHDR(rc));
470}
471
472/*
473 * This is a hand-coded derivative of the automatically generated
474 * (un)marshalling routine for bind_ack headers. bind_ack headers
475 * have an interior conformant array, which is inconsistent with
476 * IDL/NDR rules.
477 */
478extern struct ndr_typeinfo ndt__uchar;
479extern struct ndr_typeinfo ndt__ushort;
480extern struct ndr_typeinfo ndt__ulong;
481
482int ndr__ndr_bind_ack_hdr(ndr_ref_t *encl_ref);
483ndr_typeinfo_t ndt__ndr_bind_ack_hdr = {
484    1,		/* NDR version */
485    3,		/* alignment */
486    NDR_F_STRUCT,	/* flags */
487    ndr__ndr_bind_ack_hdr,	/* ndr_func */
488    68,		/* pdu_size_fixed_part */
489    0,		/* pdu_size_variable_part */
490    68,		/* c_size_fixed_part */
491    0,		/* c_size_variable_part */
492};
493
494/*
495 * [_no_reorder]
496 */
497int
498ndr__ndr_bind_ack_hdr(ndr_ref_t *encl_ref)
499{
500	ndr_stream_t		*nds = encl_ref->stream;
501	struct ndr_bind_ack_hdr	*val = /*LINTED E_BAD_PTR_CAST_ALIGN*/
502	    (struct ndr_bind_ack_hdr *)encl_ref->datum;
503	ndr_ref_t		myref;
504	unsigned long		offset;
505
506	bzero(&myref, sizeof (myref));
507	myref.enclosing = encl_ref;
508	myref.stream = encl_ref->stream;
509	myref.packed_alignment = 0;
510
511	/* do all members in order */
512	NDR_MEMBER(_ndr_common_header, common_hdr, 0UL);
513	NDR_MEMBER(_ushort, max_xmit_frag, 16UL);
514	NDR_MEMBER(_ushort, max_recv_frag, 18UL);
515	NDR_MEMBER(_ulong, assoc_group_id, 20UL);
516
517	/* port any is the conformant culprit */
518	offset = 24UL;
519
520	switch (nds->m_op) {
521	case NDR_M_OP_MARSHALL:
522		val->sec_addr.length =
523		    strlen((char *)val->sec_addr.port_spec) + 1;
524		break;
525
526	case NDR_M_OP_UNMARSHALL:
527		break;
528
529	default:
530		NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID);
531		return (0);
532	}
533
534	NDR_MEMBER(_ushort, sec_addr.length, offset);
535	NDR_MEMBER_ARR_WITH_DIMENSION(_uchar, sec_addr.port_spec,
536	    offset+2UL, val->sec_addr.length);
537
538	offset += 2;
539	offset += val->sec_addr.length;
540	offset += NDR_ALIGN4(offset);
541
542	NDR_MEMBER(_ndr_p_result_list, p_result_list, offset);
543	return (1);
544}
545
546/*
547 * Assume a single presentation context element in the result list.
548 */
549unsigned
550ndr_bind_ack_hdr_size(ndr_xa_t *mxa)
551{
552	ndr_bind_ack_hdr_t *bahdr = &mxa->send_hdr.bind_ack_hdr;
553	unsigned	offset;
554	unsigned	length;
555
556	/* port any is the conformant culprit */
557	offset = 24UL;
558
559	length = strlen((char *)bahdr->sec_addr.port_spec) + 1;
560
561	offset += 2;
562	offset += length;
563	offset += NDR_ALIGN4(offset);
564	offset += sizeof (ndr_p_result_list_t);
565	return (offset);
566}
567
568/*
569 * This is a hand-coded derivative of the automatically generated
570 * (un)marshalling routine for alter_context_rsp headers.
571 * Alter context response headers have an interior conformant array,
572 * which is inconsistent with IDL/NDR rules.
573 */
574int ndr__ndr_alter_context_rsp_hdr(ndr_ref_t *encl_ref);
575ndr_typeinfo_t ndt__ndr_alter_context_rsp_hdr = {
576    1,			/* NDR version */
577    3,			/* alignment */
578    NDR_F_STRUCT,	/* flags */
579    ndr__ndr_alter_context_rsp_hdr,	/* ndr_func */
580    56,			/* pdu_size_fixed_part */
581    0,			/* pdu_size_variable_part */
582    56,			/* c_size_fixed_part */
583    0,			/* c_size_variable_part */
584};
585
586/*
587 * [_no_reorder]
588 */
589int
590ndr__ndr_alter_context_rsp_hdr(ndr_ref_t *encl_ref)
591{
592	ndr_stream_t		*nds = encl_ref->stream;
593	ndr_alter_context_rsp_hdr_t *val = /*LINTED E_BAD_PTR_CAST_ALIGN*/
594	    (ndr_alter_context_rsp_hdr_t *)encl_ref->datum;
595	ndr_ref_t		myref;
596	unsigned long		offset;
597
598	bzero(&myref, sizeof (myref));
599	myref.enclosing = encl_ref;
600	myref.stream = encl_ref->stream;
601	myref.packed_alignment = 0;
602
603	/* do all members in order */
604	NDR_MEMBER(_ndr_common_header, common_hdr, 0UL);
605	NDR_MEMBER(_ushort, max_xmit_frag, 16UL);
606	NDR_MEMBER(_ushort, max_recv_frag, 18UL);
607	NDR_MEMBER(_ulong, assoc_group_id, 20UL);
608
609	offset = 24UL;	/* offset of sec_addr */
610
611	switch (nds->m_op) {
612	case NDR_M_OP_MARSHALL:
613		val->sec_addr.length = 0;
614		break;
615
616	case NDR_M_OP_UNMARSHALL:
617		break;
618
619	default:
620		NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID);
621		return (0);
622	}
623
624	NDR_MEMBER(_ushort, sec_addr.length, offset);
625	NDR_MEMBER_ARR_WITH_DIMENSION(_uchar, sec_addr.port_spec,
626	    offset+2UL, val->sec_addr.length);
627
628	offset += 2;	/* sizeof (sec_addr.length) */
629	offset += NDR_ALIGN4(offset);
630
631	NDR_MEMBER(_ndr_p_result_list, p_result_list, offset);
632	return (1);
633}
634
635/*
636 * Assume a single presentation context element in the result list.
637 */
638unsigned
639ndr_alter_context_rsp_hdr_size(void)
640{
641	unsigned	offset;
642
643	offset = 24UL;	/* offset of sec_addr */
644	offset += 2;	/* sizeof (sec_addr.length) */
645	offset += NDR_ALIGN4(offset);
646	offset += sizeof (ndr_p_result_list_t);
647	return (offset);
648}
649