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