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 <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 void
    338 ndr_show_hdr(ndr_common_header_t *hdr)
    339 {
    340 	char	*fragtype;
    341 
    342 	if (hdr == NULL) {
    343 		ndo_printf(NULL, NULL, "ndr hdr: <null>");
    344 		return;
    345 	}
    346 
    347 	if (NDR_IS_SINGLE_FRAG(hdr->pfc_flags))
    348 		fragtype = "single";
    349 	else if (NDR_IS_FIRST_FRAG(hdr->pfc_flags))
    350 		fragtype = "first";
    351 	else if (NDR_IS_LAST_FRAG(hdr->pfc_flags))
    352 		fragtype = "last";
    353 	else
    354 		fragtype = "intermediate";
    355 
    356 	ndo_printf(NULL, NULL,
    357 	    "ndr hdr: %d.%d ptype=%d, %s frag (flags=0x%08x) len=%d",
    358 	    hdr->rpc_vers, hdr->rpc_vers_minor, hdr->ptype,
    359 	    fragtype, hdr->pfc_flags, hdr->frag_length);
    360 }
    361 
    362 int
    363 ndr_encode_pdu_hdr(ndr_xa_t *mxa)
    364 {
    365 	ndr_common_header_t	*hdr = &mxa->send_hdr.common_hdr;
    366 	ndr_stream_t		*nds = &mxa->send_nds;
    367 	int			ptype;
    368 	int			rc;
    369 
    370 	if (nds->m_op != NDR_M_OP_MARSHALL)
    371 		return (NDR_DRC_PTYPE_RPCHDR(NDR_DRC_FAULT_MODE_MISMATCH));
    372 
    373 	ptype = hdr->ptype;
    374 	if (ptype == NDR_PTYPE_REQUEST &&
    375 	    (hdr->pfc_flags & NDR_PFC_OBJECT_UUID) != 0) {
    376 		ptype = NDR_PTYPE_REQUEST_WITH;	/* fake for sizing */
    377 	}
    378 
    379 	rc = ndr_encode_decode_common(nds, ptype, &TYPEINFO(ndr_hdr), hdr);
    380 
    381 	return (NDR_DRC_PTYPE_RPCHDR(rc));
    382 }
    383 
    384 /*
    385  * This is a hand-coded derivative of the automatically generated
    386  * (un)marshalling routine for bind_ack headers. bind_ack headers
    387  * have an interior conformant array, which is inconsistent with
    388  * IDL/NDR rules.
    389  */
    390 extern struct ndr_typeinfo ndt__uchar;
    391 extern struct ndr_typeinfo ndt__ushort;
    392 extern struct ndr_typeinfo ndt__ulong;
    393 
    394 int ndr__ndr_bind_ack_hdr(ndr_ref_t *encl_ref);
    395 ndr_typeinfo_t ndt__ndr_bind_ack_hdr = {
    396     1,		/* NDR version */
    397     3,		/* alignment */
    398     NDR_F_STRUCT,	/* flags */
    399     ndr__ndr_bind_ack_hdr,	/* ndr_func */
    400     68,		/* pdu_size_fixed_part */
    401     0,		/* pdu_size_variable_part */
    402     68,		/* c_size_fixed_part */
    403     0,		/* c_size_variable_part */
    404 };
    405 
    406 /*
    407  * [_no_reorder]
    408  */
    409 int
    410 ndr__ndr_bind_ack_hdr(ndr_ref_t *encl_ref)
    411 {
    412 	ndr_stream_t		*nds = encl_ref->stream;
    413 	struct ndr_bind_ack_hdr	*val = /*LINTED E_BAD_PTR_CAST_ALIGN*/
    414 	    (struct ndr_bind_ack_hdr *)encl_ref->datum;
    415 	ndr_ref_t		myref;
    416 	unsigned long		offset;
    417 
    418 	bzero(&myref, sizeof (myref));
    419 	myref.enclosing = encl_ref;
    420 	myref.stream = encl_ref->stream;
    421 	myref.packed_alignment = 0;
    422 
    423 	/* do all members in order */
    424 	NDR_MEMBER(_ndr_common_header, common_hdr, 0UL);
    425 	NDR_MEMBER(_ushort, max_xmit_frag, 16UL);
    426 	NDR_MEMBER(_ushort, max_recv_frag, 18UL);
    427 	NDR_MEMBER(_ulong, assoc_group_id, 20UL);
    428 
    429 	/* port any is the conformant culprit */
    430 	offset = 24UL;
    431 
    432 	switch (nds->m_op) {
    433 	case NDR_M_OP_MARSHALL:
    434 		val->sec_addr.length =
    435 		    strlen((char *)val->sec_addr.port_spec) + 1;
    436 		break;
    437 
    438 	case NDR_M_OP_UNMARSHALL:
    439 		break;
    440 
    441 	default:
    442 		NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID);
    443 		return (0);
    444 	}
    445 
    446 	NDR_MEMBER(_ushort, sec_addr.length, offset);
    447 	NDR_MEMBER_ARR_WITH_DIMENSION(_uchar, sec_addr.port_spec,
    448 	    offset+2UL, val->sec_addr.length);
    449 
    450 	offset += 2;
    451 	offset += val->sec_addr.length;
    452 	offset += NDR_ALIGN4(offset);
    453 
    454 	NDR_MEMBER(_ndr_p_result_list, p_result_list, offset);
    455 	return (1);
    456 }
    457 
    458 /*
    459  * Assume a single presentation context element in the result list.
    460  */
    461 unsigned
    462 ndr_bind_ack_hdr_size(ndr_xa_t *mxa)
    463 {
    464 	ndr_bind_ack_hdr_t *bahdr = &mxa->send_hdr.bind_ack_hdr;
    465 	unsigned	offset;
    466 	unsigned	length;
    467 
    468 	/* port any is the conformant culprit */
    469 	offset = 24UL;
    470 
    471 	length = strlen((char *)bahdr->sec_addr.port_spec) + 1;
    472 
    473 	offset += 2;
    474 	offset += length;
    475 	offset += NDR_ALIGN4(offset);
    476 	offset += sizeof (ndr_p_result_list_t);
    477 	return (offset);
    478 }
    479 
    480 /*
    481  * This is a hand-coded derivative of the automatically generated
    482  * (un)marshalling routine for alter_context_rsp headers.
    483  * Alter context response headers have an interior conformant array,
    484  * which is inconsistent with IDL/NDR rules.
    485  */
    486 int ndr__ndr_alter_context_rsp_hdr(ndr_ref_t *encl_ref);
    487 ndr_typeinfo_t ndt__ndr_alter_context_rsp_hdr = {
    488     1,			/* NDR version */
    489     3,			/* alignment */
    490     NDR_F_STRUCT,	/* flags */
    491     ndr__ndr_alter_context_rsp_hdr,	/* ndr_func */
    492     56,			/* pdu_size_fixed_part */
    493     0,			/* pdu_size_variable_part */
    494     56,			/* c_size_fixed_part */
    495     0,			/* c_size_variable_part */
    496 };
    497 
    498 /*
    499  * [_no_reorder]
    500  */
    501 int
    502 ndr__ndr_alter_context_rsp_hdr(ndr_ref_t *encl_ref)
    503 {
    504 	ndr_stream_t		*nds = encl_ref->stream;
    505 	ndr_alter_context_rsp_hdr_t *val = /*LINTED E_BAD_PTR_CAST_ALIGN*/
    506 	    (ndr_alter_context_rsp_hdr_t *)encl_ref->datum;
    507 	ndr_ref_t		myref;
    508 	unsigned long		offset;
    509 
    510 	bzero(&myref, sizeof (myref));
    511 	myref.enclosing = encl_ref;
    512 	myref.stream = encl_ref->stream;
    513 	myref.packed_alignment = 0;
    514 
    515 	/* do all members in order */
    516 	NDR_MEMBER(_ndr_common_header, common_hdr, 0UL);
    517 	NDR_MEMBER(_ushort, max_xmit_frag, 16UL);
    518 	NDR_MEMBER(_ushort, max_recv_frag, 18UL);
    519 	NDR_MEMBER(_ulong, assoc_group_id, 20UL);
    520 
    521 	offset = 24UL;	/* offset of sec_addr */
    522 
    523 	switch (nds->m_op) {
    524 	case NDR_M_OP_MARSHALL:
    525 		val->sec_addr.length = 0;
    526 		break;
    527 
    528 	case NDR_M_OP_UNMARSHALL:
    529 		break;
    530 
    531 	default:
    532 		NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID);
    533 		return (0);
    534 	}
    535 
    536 	NDR_MEMBER(_ushort, sec_addr.length, offset);
    537 	NDR_MEMBER_ARR_WITH_DIMENSION(_uchar, sec_addr.port_spec,
    538 	    offset+2UL, val->sec_addr.length);
    539 
    540 	offset += 2;	/* sizeof (sec_addr.length) */
    541 	offset += NDR_ALIGN4(offset);
    542 
    543 	NDR_MEMBER(_ndr_p_result_list, p_result_list, offset);
    544 	return (1);
    545 }
    546 
    547 /*
    548  * Assume a single presentation context element in the result list.
    549  */
    550 unsigned
    551 ndr_alter_context_rsp_hdr_size(void)
    552 {
    553 	unsigned	offset;
    554 
    555 	offset = 24UL;	/* offset of sec_addr */
    556 	offset += 2;	/* sizeof (sec_addr.length) */
    557 	offset += NDR_ALIGN4(offset);
    558 	offset += sizeof (ndr_p_result_list_t);
    559 	return (offset);
    560 }
    561