xref: /titanic_41/usr/src/lib/smbsrv/libmlrpc/common/ndr_marshal.c (revision 9e37f2b5d225bd6428d62c06655138fc78fd1ac0)
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 
26 #include <assert.h>
27 #include <strings.h>
28 #include <sys/param.h>
29 
30 #include <smbsrv/libsmb.h>
31 #include <smbsrv/libmlrpc.h>
32 
33 #ifdef _BIG_ENDIAN
34 static const int ndr_native_byte_order = NDR_REPLAB_INTG_BIG_ENDIAN;
35 #else
36 static const int ndr_native_byte_order = NDR_REPLAB_INTG_LITTLE_ENDIAN;
37 #endif
38 
39 static int ndr_decode_hdr_common(ndr_stream_t *, ndr_common_header_t *);
40 
41 static int
42 ndr_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 
81 ndr_buf_t *
82 ndr_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 
99 void
100 ndr_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  */
124 int
125 ndr_buf_decode(ndr_buf_t *nbuf, unsigned opnum, const char *data,
126     size_t datalen, void *result)
127 {
128 	ndr_common_header_t	hdr;
129 	unsigned		pdu_size_hint;
130 	int			rc;
131 
132 	assert(nbuf->nb_magic == NDR_BUF_MAGIC);
133 	assert(nbuf->nb_heap != NULL);
134 	assert(nbuf->nb_ti != NULL);
135 
136 	if (datalen < NDR_PDU_SIZE_HINT_DEFAULT)
137 		pdu_size_hint = NDR_PDU_SIZE_HINT_DEFAULT;
138 	else
139 		pdu_size_hint = datalen;
140 
141 	rc = nds_initialize(&nbuf->nb_nds, pdu_size_hint, NDR_MODE_BUF_DECODE,
142 	    nbuf->nb_heap);
143 	if (NDR_DRC_IS_FAULT(rc))
144 		return (rc);
145 
146 	bcopy(data, nbuf->nb_nds.pdu_base_addr, datalen);
147 
148 	rc = ndr_decode_hdr_common(&nbuf->nb_nds, &hdr);
149 	if (NDR_DRC_IS_FAULT(rc))
150 		return (rc);
151 
152 	if (!NDR_IS_SINGLE_FRAG(hdr.pfc_flags))
153 		return (NDR_DRC_FAULT_DECODE_FAILED);
154 
155 	rc = ndr_encode_decode_common(&nbuf->nb_nds, opnum, nbuf->nb_ti,
156 	    result);
157 	return (rc);
158 }
159 
160 /*
161  * Use the receive stream to unmarshall data (NDR_MODE_CALL_RECV).
162  */
163 int
164 ndr_decode_call(ndr_xa_t *mxa, void *params)
165 {
166 	ndr_stream_t	*nds = &mxa->recv_nds;
167 	int		rc;
168 
169 	if (!NDR_MODE_MATCH(nds, NDR_MODE_CALL_RECV))
170 		return (NDR_DRC_FAULT_MODE_MISMATCH);
171 
172 	rc = ndr_encode_decode_common(nds, mxa->opnum,
173 	    mxa->binding->service->interface_ti, params);
174 
175 	return (rc + NDR_PTYPE_REQUEST);
176 }
177 
178 /*
179  * Use the send stream to marshall data (NDR_MODE_RETURN_SEND).
180  */
181 int
182 ndr_encode_return(ndr_xa_t *mxa, void *params)
183 {
184 	ndr_stream_t	*nds = &mxa->send_nds;
185 	int		rc;
186 
187 	if (!NDR_MODE_MATCH(nds, NDR_MODE_RETURN_SEND))
188 		return (NDR_DRC_FAULT_MODE_MISMATCH);
189 
190 	rc = ndr_encode_decode_common(nds, mxa->opnum,
191 	    mxa->binding->service->interface_ti, params);
192 
193 	return (rc + NDR_PTYPE_RESPONSE);
194 }
195 
196 /*
197  * Use the send stream to marshall data (NDR_MODE_CALL_SEND).
198  */
199 int
200 ndr_encode_call(ndr_xa_t *mxa, void *params)
201 {
202 	ndr_stream_t	*nds = &mxa->send_nds;
203 	int		rc;
204 
205 	if (!NDR_MODE_MATCH(nds, NDR_MODE_CALL_SEND))
206 		return (NDR_DRC_FAULT_MODE_MISMATCH);
207 
208 	rc = ndr_encode_decode_common(nds, mxa->opnum,
209 	    mxa->binding->service->interface_ti, params);
210 
211 	return (rc + NDR_PTYPE_REQUEST);
212 }
213 
214 /*
215  * Use the receive stream to unmarshall data (NDR_MODE_RETURN_RECV).
216  */
217 int
218 ndr_decode_return(ndr_xa_t *mxa, void *params)
219 {
220 	ndr_stream_t	*nds = &mxa->recv_nds;
221 	int		rc;
222 
223 	if (!NDR_MODE_MATCH(nds, NDR_MODE_RETURN_RECV))
224 		return (NDR_DRC_FAULT_MODE_MISMATCH);
225 
226 	rc = ndr_encode_decode_common(nds, mxa->opnum,
227 	    mxa->binding->service->interface_ti, params);
228 
229 	return (rc + NDR_PTYPE_RESPONSE);
230 }
231 
232 int
233 ndr_decode_pdu_hdr(ndr_xa_t *mxa)
234 {
235 	ndr_common_header_t	*hdr = &mxa->recv_hdr.common_hdr;
236 	ndr_stream_t		*nds = &mxa->recv_nds;
237 	int			rc;
238 
239 	rc = ndr_decode_hdr_common(nds, hdr);
240 	if (NDR_DRC_IS_FAULT(rc))
241 		return (rc);
242 
243 	/*
244 	 * Verify the protocol version.
245 	 */
246 	if ((hdr->rpc_vers != 5) || (hdr->rpc_vers_minor != 0))
247 		return (NDR_DRC_PTYPE_RPCHDR(NDR_DRC_FAULT_DECODE_FAILED));
248 
249 	mxa->ptype = hdr->ptype;
250 	return (NDR_DRC_OK);
251 }
252 
253 static int
254 ndr_decode_hdr_common(ndr_stream_t *nds, ndr_common_header_t *hdr)
255 {
256 	int			ptype;
257 	int			rc;
258 	int			charset;
259 	int			byte_order;
260 
261 	if (nds->m_op != NDR_M_OP_UNMARSHALL)
262 		return (NDR_DRC_PTYPE_RPCHDR(NDR_DRC_FAULT_MODE_MISMATCH));
263 
264 	/*
265 	 * All PDU headers are at least this big
266 	 */
267 	rc = NDS_GROW_PDU(nds, sizeof (ndr_common_header_t), 0);
268 	if (!rc)
269 		return (NDR_DRC_PTYPE_RPCHDR(NDR_DRC_FAULT_RECEIVED_RUNT));
270 
271 	/*
272 	 * Peek at the first eight bytes to figure out what we're doing.
273 	 */
274 	rc = NDS_GET_PDU(nds, 0, 8, (char *)hdr, 0, 0);
275 	if (!rc)
276 		return (NDR_DRC_PTYPE_RPCHDR(NDR_DRC_FAULT_DECODE_FAILED));
277 
278 	/*
279 	 * Check for ASCII as the character set.  This is an ASCII
280 	 * versus EBCDIC option and has nothing to do with Unicode.
281 	 */
282 	charset = hdr->packed_drep.intg_char_rep & NDR_REPLAB_CHAR_MASK;
283 	if (charset != NDR_REPLAB_CHAR_ASCII)
284 		return (NDR_DRC_PTYPE_RPCHDR(NDR_DRC_FAULT_DECODE_FAILED));
285 
286 	/*
287 	 * Set the byte swap flag if the PDU byte-order
288 	 * is different from the local byte-order.
289 	 */
290 	byte_order = hdr->packed_drep.intg_char_rep & NDR_REPLAB_INTG_MASK;
291 	nds->swap = (byte_order != ndr_native_byte_order) ? 1 : 0;
292 
293 	ptype = hdr->ptype;
294 	if (ptype == NDR_PTYPE_REQUEST &&
295 	    (hdr->pfc_flags & NDR_PFC_OBJECT_UUID) != 0) {
296 		ptype = NDR_PTYPE_REQUEST_WITH;	/* fake for sizing */
297 	}
298 
299 	rc = ndr_encode_decode_common(nds, ptype, &TYPEINFO(ndr_hdr), hdr);
300 
301 	return (NDR_DRC_PTYPE_RPCHDR(rc));
302 }
303 
304 /*
305  * Decode an RPC fragment header.  Use ndr_decode_pdu_hdr() to process
306  * the first fragment header then this function to process additional
307  * fragment headers.
308  */
309 void
310 ndr_decode_frag_hdr(ndr_stream_t *nds, ndr_common_header_t *hdr)
311 {
312 	ndr_common_header_t *tmp;
313 	uint8_t *pdu;
314 	int byte_order;
315 
316 	pdu = (uint8_t *)nds->pdu_base_offset + nds->pdu_scan_offset;
317 	bcopy(pdu, hdr, NDR_RSP_HDR_SIZE);
318 
319 	/*
320 	 * Swap non-byte fields if the PDU byte-order
321 	 * is different from the local byte-order.
322 	 */
323 	byte_order = hdr->packed_drep.intg_char_rep & NDR_REPLAB_INTG_MASK;
324 
325 	if (byte_order != ndr_native_byte_order) {
326 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
327 		tmp = (ndr_common_header_t *)pdu;
328 
329 		nds_bswap(&tmp->frag_length, &hdr->frag_length,
330 		    sizeof (WORD));
331 		nds_bswap(&tmp->auth_length, &hdr->auth_length,
332 		    sizeof (WORD));
333 		nds_bswap(&tmp->call_id, &hdr->call_id, sizeof (DWORD));
334 	}
335 }
336 
337 /*
338  * Remove an RPC fragment header from the received data stream.
339  *
340  * NDR stream on entry:
341  *
342  *                |<--- frag --->|
343  * +-----+--------+-----+--------+-----+---------+-----+
344  * | hdr |  data  | hdr |  data  | hdr |  data   | ... |
345  * +-----+--------+-----+--------+-----+---------+-----+
346  *                 <----
347  *
348  * NDR stream on return:
349  *
350  * +-----+----------------+-----+---------+-----+
351  * | hdr |       data     | hdr |  data   | ... |
352  * +-----+----------------+-----+---------+-----+
353  */
354 void
355 ndr_remove_frag_hdr(ndr_stream_t *nds)
356 {
357 	char	*hdr;
358 	char	*data;
359 	int	nbytes;
360 
361 	hdr = (char *)nds->pdu_base_offset + nds->pdu_scan_offset;
362 	data = hdr + NDR_RSP_HDR_SIZE;
363 	nbytes = nds->pdu_size - nds->pdu_scan_offset - NDR_RSP_HDR_SIZE;
364 
365 	bcopy(data, hdr, nbytes);
366 	nds->pdu_size -= NDR_RSP_HDR_SIZE;
367 }
368 
369 void
370 ndr_show_hdr(ndr_common_header_t *hdr)
371 {
372 	char	*fragtype;
373 
374 	if (hdr == NULL) {
375 		ndo_printf(NULL, NULL, "ndr hdr: <null>");
376 		return;
377 	}
378 
379 	if (NDR_IS_SINGLE_FRAG(hdr->pfc_flags))
380 		fragtype = "single";
381 	else if (NDR_IS_FIRST_FRAG(hdr->pfc_flags))
382 		fragtype = "first";
383 	else if (NDR_IS_LAST_FRAG(hdr->pfc_flags))
384 		fragtype = "last";
385 	else
386 		fragtype = "intermediate";
387 
388 	ndo_printf(NULL, NULL,
389 	    "ndr hdr: %d.%d ptype=%d, %s frag (flags=0x%08x) len=%d",
390 	    hdr->rpc_vers, hdr->rpc_vers_minor, hdr->ptype,
391 	    fragtype, hdr->pfc_flags, hdr->frag_length);
392 }
393 
394 int
395 ndr_encode_pdu_hdr(ndr_xa_t *mxa)
396 {
397 	ndr_common_header_t	*hdr = &mxa->send_hdr.common_hdr;
398 	ndr_stream_t		*nds = &mxa->send_nds;
399 	int			ptype;
400 	int			rc;
401 
402 	if (nds->m_op != NDR_M_OP_MARSHALL)
403 		return (NDR_DRC_PTYPE_RPCHDR(NDR_DRC_FAULT_MODE_MISMATCH));
404 
405 	ptype = hdr->ptype;
406 	if (ptype == NDR_PTYPE_REQUEST &&
407 	    (hdr->pfc_flags & NDR_PFC_OBJECT_UUID) != 0) {
408 		ptype = NDR_PTYPE_REQUEST_WITH;	/* fake for sizing */
409 	}
410 
411 	rc = ndr_encode_decode_common(nds, ptype, &TYPEINFO(ndr_hdr), hdr);
412 
413 	return (NDR_DRC_PTYPE_RPCHDR(rc));
414 }
415 
416 /*
417  * This is a hand-coded derivative of the automatically generated
418  * (un)marshalling routine for bind_ack headers. bind_ack headers
419  * have an interior conformant array, which is inconsistent with
420  * IDL/NDR rules.
421  */
422 extern struct ndr_typeinfo ndt__uchar;
423 extern struct ndr_typeinfo ndt__ushort;
424 extern struct ndr_typeinfo ndt__ulong;
425 
426 int ndr__ndr_bind_ack_hdr(ndr_ref_t *encl_ref);
427 ndr_typeinfo_t ndt__ndr_bind_ack_hdr = {
428     1,		/* NDR version */
429     3,		/* alignment */
430     NDR_F_STRUCT,	/* flags */
431     ndr__ndr_bind_ack_hdr,	/* ndr_func */
432     68,		/* pdu_size_fixed_part */
433     0,		/* pdu_size_variable_part */
434     68,		/* c_size_fixed_part */
435     0,		/* c_size_variable_part */
436 };
437 
438 /*
439  * [_no_reorder]
440  */
441 int
442 ndr__ndr_bind_ack_hdr(ndr_ref_t *encl_ref)
443 {
444 	ndr_stream_t		*nds = encl_ref->stream;
445 	struct ndr_bind_ack_hdr	*val = /*LINTED E_BAD_PTR_CAST_ALIGN*/
446 	    (struct ndr_bind_ack_hdr *)encl_ref->datum;
447 	ndr_ref_t		myref;
448 	unsigned long		offset;
449 
450 	bzero(&myref, sizeof (myref));
451 	myref.enclosing = encl_ref;
452 	myref.stream = encl_ref->stream;
453 	myref.packed_alignment = 0;
454 
455 	/* do all members in order */
456 	NDR_MEMBER(_ndr_common_header, common_hdr, 0UL);
457 	NDR_MEMBER(_ushort, max_xmit_frag, 16UL);
458 	NDR_MEMBER(_ushort, max_recv_frag, 18UL);
459 	NDR_MEMBER(_ulong, assoc_group_id, 20UL);
460 
461 	/* port any is the conformant culprit */
462 	offset = 24UL;
463 
464 	switch (nds->m_op) {
465 	case NDR_M_OP_MARSHALL:
466 		val->sec_addr.length =
467 		    strlen((char *)val->sec_addr.port_spec) + 1;
468 		break;
469 
470 	case NDR_M_OP_UNMARSHALL:
471 		break;
472 
473 	default:
474 		NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID);
475 		return (0);
476 	}
477 
478 	NDR_MEMBER(_ushort, sec_addr.length, offset);
479 	NDR_MEMBER_ARR_WITH_DIMENSION(_uchar, sec_addr.port_spec,
480 	    offset+2UL, val->sec_addr.length);
481 
482 	offset += 2;
483 	offset += val->sec_addr.length;
484 	offset += NDR_ALIGN4(offset);
485 
486 	NDR_MEMBER(_ndr_p_result_list, p_result_list, offset);
487 	return (1);
488 }
489 
490 /*
491  * Assume a single presentation context element in the result list.
492  */
493 unsigned
494 ndr_bind_ack_hdr_size(ndr_xa_t *mxa)
495 {
496 	ndr_bind_ack_hdr_t *bahdr = &mxa->send_hdr.bind_ack_hdr;
497 	unsigned	offset;
498 	unsigned	length;
499 
500 	/* port any is the conformant culprit */
501 	offset = 24UL;
502 
503 	length = strlen((char *)bahdr->sec_addr.port_spec) + 1;
504 
505 	offset += 2;
506 	offset += length;
507 	offset += NDR_ALIGN4(offset);
508 	offset += sizeof (ndr_p_result_list_t);
509 	return (offset);
510 }
511 
512 /*
513  * This is a hand-coded derivative of the automatically generated
514  * (un)marshalling routine for alter_context_rsp headers.
515  * Alter context response headers have an interior conformant array,
516  * which is inconsistent with IDL/NDR rules.
517  */
518 int ndr__ndr_alter_context_rsp_hdr(ndr_ref_t *encl_ref);
519 ndr_typeinfo_t ndt__ndr_alter_context_rsp_hdr = {
520     1,			/* NDR version */
521     3,			/* alignment */
522     NDR_F_STRUCT,	/* flags */
523     ndr__ndr_alter_context_rsp_hdr,	/* ndr_func */
524     56,			/* pdu_size_fixed_part */
525     0,			/* pdu_size_variable_part */
526     56,			/* c_size_fixed_part */
527     0,			/* c_size_variable_part */
528 };
529 
530 /*
531  * [_no_reorder]
532  */
533 int
534 ndr__ndr_alter_context_rsp_hdr(ndr_ref_t *encl_ref)
535 {
536 	ndr_stream_t		*nds = encl_ref->stream;
537 	ndr_alter_context_rsp_hdr_t *val = /*LINTED E_BAD_PTR_CAST_ALIGN*/
538 	    (ndr_alter_context_rsp_hdr_t *)encl_ref->datum;
539 	ndr_ref_t		myref;
540 	unsigned long		offset;
541 
542 	bzero(&myref, sizeof (myref));
543 	myref.enclosing = encl_ref;
544 	myref.stream = encl_ref->stream;
545 	myref.packed_alignment = 0;
546 
547 	/* do all members in order */
548 	NDR_MEMBER(_ndr_common_header, common_hdr, 0UL);
549 	NDR_MEMBER(_ushort, max_xmit_frag, 16UL);
550 	NDR_MEMBER(_ushort, max_recv_frag, 18UL);
551 	NDR_MEMBER(_ulong, assoc_group_id, 20UL);
552 
553 	offset = 24UL;	/* offset of sec_addr */
554 
555 	switch (nds->m_op) {
556 	case NDR_M_OP_MARSHALL:
557 		val->sec_addr.length = 0;
558 		break;
559 
560 	case NDR_M_OP_UNMARSHALL:
561 		break;
562 
563 	default:
564 		NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID);
565 		return (0);
566 	}
567 
568 	NDR_MEMBER(_ushort, sec_addr.length, offset);
569 	NDR_MEMBER_ARR_WITH_DIMENSION(_uchar, sec_addr.port_spec,
570 	    offset+2UL, val->sec_addr.length);
571 
572 	offset += 2;	/* sizeof (sec_addr.length) */
573 	offset += NDR_ALIGN4(offset);
574 
575 	NDR_MEMBER(_ndr_p_result_list, p_result_list, offset);
576 	return (1);
577 }
578 
579 /*
580  * Assume a single presentation context element in the result list.
581  */
582 unsigned
583 ndr_alter_context_rsp_hdr_size(void)
584 {
585 	unsigned	offset;
586 
587 	offset = 24UL;	/* offset of sec_addr */
588 	offset += 2;	/* sizeof (sec_addr.length) */
589 	offset += NDR_ALIGN4(offset);
590 	offset += sizeof (ndr_p_result_list_t);
591 	return (offset);
592 }
593