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