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 * Copyright 2020 Tintri by DDN, Inc. All rights reserved. 26 */ 27 28 #include <sys/errno.h> 29 #include <string.h> 30 #include <strings.h> 31 32 #include <libmlrpc.h> 33 34 #define NDR_DEFAULT_FRAGSZ 8192 35 #define NDR_MULTI_FRAGSZ (60 * 1024) 36 37 static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_t *); 38 static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *); 39 static int ndr_clnt_get_frag(ndr_client_t *, ndr_xa_t *, ndr_common_header_t *); 40 41 int 42 ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc, 43 ndr_binding_t **ret_binding_p) 44 { 45 ndr_binding_t *mbind; 46 ndr_xa_t mxa; 47 ndr_bind_hdr_t *bhdr; 48 ndr_common_header_t *hdr; 49 ndr_p_cont_elem_t *pce; 50 ndr_bind_ack_hdr_t *bahdr; 51 ndr_p_result_t *pre; 52 int rc; 53 54 bzero(&mxa, sizeof (mxa)); 55 56 mxa.binding_list = clnt->binding_list; 57 if ((mbind = ndr_svc_new_binding(&mxa)) == NULL) 58 return (NDR_DRC_FAULT_API_BIND_NO_SLOTS); 59 60 ndr_clnt_init_hdr(clnt, &mxa); 61 62 bhdr = &mxa.send_hdr.bind_hdr; 63 hdr = &mxa.send_hdr.bind_hdr.common_hdr; 64 hdr->ptype = NDR_PTYPE_BIND; 65 bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ; 66 bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ; 67 bhdr->assoc_group_id = 0; 68 bhdr->p_context_elem.n_context_elem = 1; 69 70 /* Assign presentation context id */ 71 pce = &bhdr->p_context_elem.p_cont_elem[0]; 72 pce->p_cont_id = clnt->next_p_cont_id++; 73 pce->n_transfer_syn = 1; 74 75 /* Set up UUIDs and versions from the service */ 76 pce->abstract_syntax.if_version = msvc->abstract_syntax_version; 77 rc = ndr_uuid_parse(msvc->abstract_syntax_uuid, 78 &pce->abstract_syntax.if_uuid); 79 if (rc != 0) 80 return (NDR_DRC_FAULT_API_SERVICE_INVALID); 81 82 pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version; 83 rc = ndr_uuid_parse(msvc->transfer_syntax_uuid, 84 &pce->transfer_syntaxes[0].if_uuid); 85 if (rc != 0) 86 return (NDR_DRC_FAULT_API_SERVICE_INVALID); 87 88 /* Format and exchange the PDU */ 89 90 if ((*clnt->xa_init)(clnt, &mxa) < 0) 91 return (NDR_DRC_FAULT_OUT_OF_MEMORY); 92 93 /* Reserve room for hdr */ 94 mxa.send_nds.pdu_scan_offset = sizeof (*bhdr); 95 96 /* GSS_Init_sec_context */ 97 rc = ndr_add_sec_context(&clnt->auth_ctx, &mxa); 98 if (NDR_DRC_IS_FAULT(rc)) 99 goto fault_exit; 100 101 rc = ndr_encode_pdu_auth(&mxa); 102 if (NDR_DRC_IS_FAULT(rc)) 103 goto fault_exit; 104 105 /* 106 * If we have auth data, then pdu_size has been initialized. 107 * Otherwise, it hasn't. 108 */ 109 if (hdr->auth_length == 0) 110 hdr->frag_length = sizeof (*bhdr); 111 else 112 hdr->frag_length = mxa.send_nds.pdu_size; 113 114 /* Reset scan_offset to header */ 115 mxa.send_nds.pdu_scan_offset = 0; 116 117 rc = ndr_encode_pdu_hdr(&mxa); 118 if (NDR_DRC_IS_FAULT(rc)) 119 goto fault_exit; 120 121 if ((*clnt->xa_exchange)(clnt, &mxa) < 0) { 122 rc = NDR_DRC_FAULT_SEND_FAILED; 123 goto fault_exit; 124 } 125 126 rc = ndr_decode_pdu_hdr(&mxa); 127 if (NDR_DRC_IS_FAULT(rc)) 128 goto fault_exit; 129 130 rc = ndr_decode_pdu_auth(&mxa); 131 if (NDR_DRC_IS_FAULT(rc)) 132 goto fault_exit; 133 134 bahdr = &mxa.recv_hdr.bind_ack_hdr; 135 136 if (mxa.ptype != NDR_PTYPE_BIND_ACK) { 137 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 138 goto fault_exit; 139 } 140 141 if (bahdr->p_result_list.n_results != 1) { 142 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 143 goto fault_exit; 144 } 145 146 pre = &bahdr->p_result_list.p_results[0]; 147 148 if (pre->result != NDR_PCDR_ACCEPTANCE) { 149 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 150 goto fault_exit; 151 } 152 153 /* GSS_init_sec_context 2 */ 154 rc = ndr_recv_sec_context(&clnt->auth_ctx, &mxa); 155 if (NDR_DRC_IS_FAULT(rc)) 156 goto fault_exit; 157 158 /* done with buffers */ 159 (*clnt->xa_destruct)(clnt, &mxa); 160 161 mbind->p_cont_id = pce->p_cont_id; 162 mbind->which_side = NDR_BIND_SIDE_CLIENT; 163 mbind->clnt = clnt; 164 mbind->service = msvc; 165 mbind->instance_specific = 0; 166 167 *ret_binding_p = mbind; 168 return (NDR_DRC_OK); 169 170 fault_exit: 171 (*clnt->xa_destruct)(clnt, &mxa); 172 return (rc); 173 } 174 175 int 176 ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params) 177 { 178 ndr_client_t *clnt = mbind->clnt; 179 ndr_xa_t mxa; 180 ndr_request_hdr_t *reqhdr; 181 ndr_common_header_t *rsphdr; 182 unsigned long recv_pdu_scan_offset, recv_pdu_size; 183 int rc; 184 185 bzero(&mxa, sizeof (mxa)); 186 mxa.ptype = NDR_PTYPE_REQUEST; 187 mxa.opnum = opnum; 188 mxa.binding = mbind; 189 190 ndr_clnt_init_hdr(clnt, &mxa); 191 192 reqhdr = &mxa.send_hdr.request_hdr; 193 reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST; 194 reqhdr->p_cont_id = mbind->p_cont_id; 195 reqhdr->opnum = opnum; 196 197 rc = (*clnt->xa_init)(clnt, &mxa); 198 if (NDR_DRC_IS_FAULT(rc)) 199 return (rc); 200 201 /* Reserve room for hdr */ 202 mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr); 203 /* pdu_scan_offset now points to start of stub */ 204 mxa.send_nds.pdu_body_offset = mxa.send_nds.pdu_scan_offset; 205 206 rc = ndr_encode_call(&mxa, params); 207 if (!NDR_DRC_IS_OK(rc)) 208 goto fault_exit; 209 210 /* 211 * With the Stub data encoded, calculate the alloc_hint 212 * before we add padding or auth data. 213 */ 214 reqhdr->alloc_hint = mxa.send_nds.pdu_size - 215 sizeof (ndr_request_hdr_t); 216 217 /* GSS_WrapEx/VerifyMICEx */ 218 rc = ndr_add_auth(&clnt->auth_ctx, &mxa); 219 if (NDR_DRC_IS_FAULT(rc)) 220 goto fault_exit; 221 222 rc = ndr_encode_pdu_auth(&mxa); 223 if (NDR_DRC_IS_FAULT(rc)) 224 goto fault_exit; 225 226 /* 227 * Now we have the PDU size, we need to set up the 228 * frag_length. 229 * Also reset pdu_scan_offset to header. 230 */ 231 mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size; 232 mxa.send_nds.pdu_scan_offset = 0; 233 234 rc = ndr_encode_pdu_hdr(&mxa); 235 if (NDR_DRC_IS_FAULT(rc)) 236 goto fault_exit; 237 238 rc = (*clnt->xa_exchange)(clnt, &mxa); 239 if (NDR_DRC_IS_FAULT(rc)) 240 goto fault_exit; 241 242 rc = ndr_decode_pdu_hdr(&mxa); 243 if (NDR_DRC_IS_FAULT(rc)) 244 goto fault_exit; 245 246 if (mxa.ptype != NDR_PTYPE_RESPONSE) { 247 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; 248 goto fault_exit; 249 } 250 251 rc = ndr_decode_pdu_auth(&mxa); 252 if (NDR_DRC_IS_FAULT(rc)) 253 goto fault_exit; 254 255 rc = ndr_check_auth(&clnt->auth_ctx, &mxa); 256 if (NDR_DRC_IS_FAULT(rc)) 257 goto fault_exit; 258 259 rsphdr = &mxa.recv_hdr.common_hdr; 260 261 if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) { 262 /* 263 * This is a multi-fragment response. 264 * Preserve the current body offset while getting 265 * fragments so that we can continue afterward 266 * as if we had received the entire response as 267 * a single PDU. 268 * 269 * GROW_PDU trashes pdu_size; reset it afterwards. 270 */ 271 recv_pdu_size = mxa.recv_nds.pdu_size; 272 (void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL); 273 274 /* 275 * pdu_scan_offset needs to be the first byte after the first 276 * fragment in pdu_base_addr (minus the sec_trailer). 277 * 278 * pdu_size needs to be all of the (usable) data we've 279 * received thus far. 280 */ 281 recv_pdu_scan_offset = mxa.recv_nds.pdu_body_offset; 282 mxa.recv_nds.pdu_scan_offset = mxa.recv_nds.pdu_body_offset + 283 mxa.recv_nds.pdu_body_size - mxa.recv_auth.auth_pad_len; 284 mxa.recv_nds.pdu_size = recv_pdu_size; 285 286 rc = ndr_clnt_get_frags(clnt, &mxa); 287 if (NDR_DRC_IS_FAULT(rc)) 288 goto fault_exit; 289 290 mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset; 291 } 292 293 rc = ndr_decode_return(&mxa, params); 294 if (NDR_DRC_IS_FAULT(rc)) 295 goto fault_exit; 296 297 (*clnt->xa_preserve)(clnt, &mxa); 298 (*clnt->xa_destruct)(clnt, &mxa); 299 return (NDR_DRC_OK); 300 301 fault_exit: 302 ndr_show_hdr(&mxa.send_hdr.common_hdr); 303 nds_show_state(&mxa.send_nds); 304 if (mxa.send_hdr.common_hdr.auth_length != 0) 305 ndr_show_auth(&mxa.send_auth); 306 307 ndr_show_hdr(&mxa.recv_hdr.common_hdr); 308 nds_show_state(&mxa.recv_nds); 309 if (mxa.recv_hdr.common_hdr.auth_length != 0) 310 ndr_show_auth(&mxa.recv_auth); 311 (*clnt->xa_destruct)(clnt, &mxa); 312 return (rc); 313 } 314 315 void 316 ndr_clnt_free_heap(ndr_client_t *clnt) 317 { 318 (*clnt->xa_release)(clnt); 319 } 320 321 static void 322 ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa) 323 { 324 ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; 325 326 hdr->rpc_vers = 5; 327 hdr->rpc_vers_minor = 0; 328 hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; 329 hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII; 330 #ifndef _BIG_ENDIAN 331 hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN; 332 #endif 333 /* hdr->frag_length */ 334 hdr->auth_length = 0; 335 hdr->call_id = clnt->next_call_id++; 336 } 337 338 /* 339 * ndr_clnt_get_frags 340 * 341 * A DCE RPC message that is larger than a single fragment is transmitted 342 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for 343 * both Windows 2000 and 2003. 344 * 345 * Collect RPC fragments and append them to the receive stream buffer. 346 * Each received fragment has a header, which we need to remove as we 347 * build the full RPC PDU. The scan offset is used to track frag headers. 348 */ 349 static int 350 ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa) 351 { 352 ndr_stream_t *nds = &mxa->recv_nds; 353 ndr_common_header_t hdr; 354 int frag_size; 355 int last_frag; 356 int rc; 357 358 do { 359 if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) { 360 nds_show_state(nds); 361 return (NDR_DRC_FAULT_RECEIVED_RUNT); 362 } 363 364 last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags); 365 frag_size = hdr.frag_length; 366 367 /* 368 * ndr_clnt_get_frag() doesn't change pdu_scan_offset. 369 */ 370 if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) { 371 nds_show_state(nds); 372 return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 373 } 374 375 if (hdr.auth_length != 0 && hdr.auth_length > 376 (hdr.frag_length - nds->pdu_hdr_size - SEC_TRAILER_SIZE)) 377 return (NDR_DRC_FAULT_RECEIVED_MALFORMED); 378 379 rc = ndr_decode_pdu_auth(mxa); 380 if (NDR_DRC_IS_FAULT(rc)) 381 return (rc); 382 383 rc = ndr_check_auth(&clnt->auth_ctx, mxa); 384 if (NDR_DRC_IS_FAULT(rc)) 385 return (rc); 386 387 /* 388 * Headers, Auth Padding, and auth data shouldn't be kept 389 * from fragments. 390 */ 391 ndr_remove_frag_hdr(nds); 392 nds->pdu_scan_offset += 393 nds->pdu_body_size - mxa->recv_auth.auth_pad_len; 394 } while (!last_frag); 395 396 return (0); 397 } 398 399 /* 400 * Read the next RPC fragment. The xa_read() calls correspond to SmbReadX 401 * requests. Note that there is no correspondence between SmbReadX buffering 402 * and DCE RPC fragment alignment. 403 */ 404 static int 405 ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr) 406 { 407 ndr_stream_t *nds = &mxa->recv_nds; 408 unsigned long available; 409 int nbytes = 0; 410 411 available = nds->pdu_size - nds->pdu_scan_offset; 412 413 while (available < NDR_RSP_HDR_SIZE) { 414 if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0) 415 return (-1); 416 available += nbytes; 417 } 418 419 ndr_decode_frag_hdr(nds, hdr); 420 ndr_show_hdr(hdr); 421 422 while (available < hdr->frag_length) { 423 if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0) 424 return (-1); 425 available += nbytes; 426 } 427 428 return (nbytes); 429 } 430