xref: /titanic_50/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c (revision dc32d872cbeb56532bcea030255db9cd79bac7da)
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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 /*
27  * Server side RPC handler.
28  */
29 
30 #include <sys/byteorder.h>
31 #include <sys/uio.h>
32 #include <errno.h>
33 #include <synch.h>
34 #include <stdlib.h>
35 #include <strings.h>
36 #include <string.h>
37 #include <thread.h>
38 
39 #include <smbsrv/libsmb.h>
40 #include <smbsrv/libmlrpc.h>
41 #include <smbsrv/ntaccess.h>
42 
43 #define	NDR_PIPE_SEND(np, buf, len) \
44 	((np)->np_send)((np), (buf), (len))
45 #define	NDR_PIPE_RECV(np, buf, len) \
46 	((np)->np_recv)((np), (buf), (len))
47 
48 static int ndr_svc_process(ndr_xa_t *);
49 static int ndr_svc_bind(ndr_xa_t *);
50 static int ndr_svc_request(ndr_xa_t *);
51 static void ndr_reply_prepare_hdr(ndr_xa_t *);
52 static int ndr_svc_alter_context(ndr_xa_t *);
53 static void ndr_reply_fault(ndr_xa_t *, unsigned long);
54 
55 static int ndr_recv_request(ndr_xa_t *mxa);
56 static int ndr_recv_frag(ndr_xa_t *mxa);
57 static int ndr_send_reply(ndr_xa_t *);
58 
59 static int ndr_pipe_process(ndr_pipe_t *, ndr_xa_t *);
60 
61 /*
62  * External entry point called by smbd.
63  */
64 void
65 ndr_pipe_worker(ndr_pipe_t *np)
66 {
67 	ndr_xa_t	*mxa;
68 	int rc;
69 
70 	ndr_svc_binding_pool_init(&np->np_binding, np->np_binding_pool,
71 	    NDR_N_BINDING_POOL);
72 
73 	if ((mxa = malloc(sizeof (*mxa))) == NULL)
74 		return;
75 
76 	do {
77 		bzero(mxa, sizeof (*mxa));
78 		rc = ndr_pipe_process(np, mxa);
79 	} while (rc == 0);
80 
81 	free(mxa);
82 
83 	/*
84 	 * Ensure that there are no RPC service policy handles
85 	 * (associated with this fid) left around.
86 	 */
87 	ndr_hdclose(np);
88 }
89 
90 /*
91  * Process one server-side RPC request.
92  */
93 static int
94 ndr_pipe_process(ndr_pipe_t *np, ndr_xa_t *mxa)
95 {
96 	ndr_stream_t	*recv_nds;
97 	ndr_stream_t	*send_nds;
98 	int		rc = ENOMEM;
99 
100 	mxa->pipe = np;
101 	mxa->binding_list = np->np_binding;
102 
103 	if ((mxa->heap = ndr_heap_create()) == NULL)
104 		goto out1;
105 
106 	recv_nds = &mxa->recv_nds;
107 	rc = nds_initialize(recv_nds, 0, NDR_MODE_CALL_RECV, mxa->heap);
108 	if (rc != 0)
109 		goto out2;
110 
111 	send_nds = &mxa->send_nds;
112 	rc = nds_initialize(send_nds, 0, NDR_MODE_RETURN_SEND, mxa->heap);
113 	if (rc != 0)
114 		goto out3;
115 
116 	rc = ndr_recv_request(mxa);
117 	if (rc != 0)
118 		goto out4;
119 
120 	(void) ndr_svc_process(mxa);
121 	(void) ndr_send_reply(mxa);
122 	rc = 0;
123 
124 out4:
125 	nds_destruct(&mxa->send_nds);
126 out3:
127 	nds_destruct(&mxa->recv_nds);
128 out2:
129 	ndr_heap_destroy(mxa->heap);
130 out1:
131 	return (rc);
132 }
133 
134 /*
135  * Check whether or not the specified user has administrator privileges,
136  * i.e. is a member of Domain Admins or Administrators.
137  * Returns true if the user is an administrator, otherwise returns false.
138  */
139 boolean_t
140 ndr_is_admin(ndr_xa_t *xa)
141 {
142 	smb_netuserinfo_t *ctx = xa->pipe->np_user;
143 
144 	return (ctx->ui_flags & SMB_ATF_ADMIN);
145 }
146 
147 /*
148  * Check whether or not the specified user has power-user privileges,
149  * i.e. is a member of Domain Admins, Administrators or Power Users.
150  * This is typically required for operations such as managing shares.
151  * Returns true if the user is a power user, otherwise returns false.
152  */
153 boolean_t
154 ndr_is_poweruser(ndr_xa_t *xa)
155 {
156 	smb_netuserinfo_t *ctx = xa->pipe->np_user;
157 
158 	return ((ctx->ui_flags & SMB_ATF_ADMIN) ||
159 	    (ctx->ui_flags & SMB_ATF_POWERUSER));
160 }
161 
162 int32_t
163 ndr_native_os(ndr_xa_t *xa)
164 {
165 	smb_netuserinfo_t *ctx = xa->pipe->np_user;
166 
167 	return (ctx->ui_native_os);
168 }
169 
170 /*
171  * Receive an entire RPC request (all fragments)
172  * Returns zero or an NDR fault code.
173  */
174 static int
175 ndr_recv_request(ndr_xa_t *mxa)
176 {
177 	ndr_common_header_t	*hdr = &mxa->recv_hdr.common_hdr;
178 	ndr_stream_t		*nds = &mxa->recv_nds;
179 	unsigned long		saved_size;
180 	int			rc;
181 
182 	rc = ndr_recv_frag(mxa);
183 	if (rc != 0)
184 		return (rc);
185 	if (!NDR_IS_FIRST_FRAG(hdr->pfc_flags))
186 		return (NDR_DRC_FAULT_DECODE_FAILED);
187 
188 	while (!NDR_IS_LAST_FRAG(hdr->pfc_flags)) {
189 		rc = ndr_recv_frag(mxa);
190 		if (rc != 0)
191 			return (rc);
192 	}
193 	nds->pdu_scan_offset = 0;
194 
195 	/*
196 	 * This whacks nds->pdu_size, so save/restore.
197 	 * It leaves scan_offset after the header.
198 	 */
199 	saved_size = nds->pdu_size;
200 	rc = ndr_decode_pdu_hdr(mxa);
201 	nds->pdu_size = saved_size;
202 
203 	return (rc);
204 }
205 
206 /*
207  * Read one fragment, leaving the decoded frag header in
208  * recv_hdr.common_hdr, and the data in the recv_nds.
209  *
210  * Returns zero or an NDR fault code.
211  *
212  * If a first frag, the header is included in the data
213  * placed in recv_nds (because it's not fully decoded
214  * until later - we only decode the common part here).
215  * Additional frags are placed in the recv_nds without
216  * the header, so that after the first frag header,
217  * the remaining data will be contiguous.  We do this
218  * by simply not advancing the offset in recv_nds after
219  * reading and decoding these additional fragments, so
220  * the payload of such frags will overwrite what was
221  * (temporarily) the frag header.
222  */
223 static int
224 ndr_recv_frag(ndr_xa_t *mxa)
225 {
226 	ndr_common_header_t	*hdr = &mxa->recv_hdr.common_hdr;
227 	ndr_stream_t		*nds = &mxa->recv_nds;
228 	unsigned char		*data;
229 	unsigned long		next_offset;
230 	unsigned long		pay_size;
231 	int			rc;
232 
233 	/* Make room for the frag header. */
234 	next_offset = nds->pdu_scan_offset + NDR_RSP_HDR_SIZE;
235 	if (!NDS_GROW_PDU(nds, next_offset, 0))
236 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
237 
238 	/* Read the frag header. */
239 	data = nds->pdu_base_addr + nds->pdu_scan_offset;
240 	rc = NDR_PIPE_RECV(mxa->pipe, data, NDR_RSP_HDR_SIZE);
241 	if (rc != 0)
242 		return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT);
243 
244 	/*
245 	 * Decode the frag header, get the length.
246 	 * NB: It uses nds->pdu_scan_offset
247 	 */
248 	ndr_decode_frag_hdr(nds, hdr);
249 	ndr_show_hdr(hdr);
250 	if (hdr->frag_length < NDR_RSP_HDR_SIZE ||
251 	    hdr->frag_length > mxa->pipe->np_max_xmit_frag)
252 		return (NDR_DRC_FAULT_DECODE_FAILED);
253 
254 	if (nds->pdu_scan_offset == 0) {
255 		/* First frag: header stays in the data. */
256 		nds->pdu_scan_offset = next_offset;
257 	} /* else overwrite with the payload */
258 
259 	/* Make room for the payload. */
260 	pay_size = hdr->frag_length - NDR_RSP_HDR_SIZE;
261 	next_offset = nds->pdu_scan_offset + pay_size;
262 	if (!NDS_GROW_PDU(nds, next_offset, 0))
263 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
264 
265 	/* Read the payload. */
266 	data = nds->pdu_base_addr + nds->pdu_scan_offset;
267 	rc = NDR_PIPE_RECV(mxa->pipe, data, pay_size);
268 	if (rc != 0)
269 		return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT);
270 	nds->pdu_scan_offset = next_offset;
271 
272 	return (NDR_DRC_OK);
273 }
274 
275 /*
276  * This is the entry point for all server-side RPC processing.
277  * It is assumed that the PDU has already been received.
278  */
279 static int
280 ndr_svc_process(ndr_xa_t *mxa)
281 {
282 	int			rc;
283 
284 	(void) ndr_reply_prepare_hdr(mxa);
285 
286 	switch (mxa->ptype) {
287 	case NDR_PTYPE_BIND:
288 		rc = ndr_svc_bind(mxa);
289 		break;
290 
291 	case NDR_PTYPE_REQUEST:
292 		rc = ndr_svc_request(mxa);
293 		break;
294 
295 	case NDR_PTYPE_ALTER_CONTEXT:
296 		rc = ndr_svc_alter_context(mxa);
297 		break;
298 
299 	default:
300 		rc = NDR_DRC_FAULT_RPCHDR_PTYPE_INVALID;
301 		break;
302 	}
303 
304 	if (NDR_DRC_IS_FAULT(rc))
305 		ndr_reply_fault(mxa, rc);
306 
307 	return (rc);
308 }
309 
310 /*
311  * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple
312  * p_results[] not supported.
313  */
314 static int
315 ndr_svc_bind(ndr_xa_t *mxa)
316 {
317 	ndr_p_cont_list_t	*cont_list;
318 	ndr_p_result_list_t	*result_list;
319 	ndr_p_result_t		*result;
320 	unsigned		p_cont_id;
321 	ndr_binding_t		*mbind;
322 	ndr_uuid_t		*as_uuid;
323 	ndr_uuid_t		*ts_uuid;
324 	int			as_vers;
325 	int			ts_vers;
326 	ndr_service_t		*msvc;
327 	int			rc;
328 	ndr_port_any_t		*sec_addr;
329 
330 	/* acquire targets */
331 	cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem;
332 	result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list;
333 	result = &result_list->p_results[0];
334 
335 	/*
336 	 * Set up temporary secondary address port.
337 	 * We will correct this later (below).
338 	 */
339 	sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
340 	sec_addr->length = 13;
341 	(void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs");
342 
343 	result_list->n_results = 1;
344 	result_list->reserved = 0;
345 	result_list->reserved2 = 0;
346 	result->result = NDR_PCDR_ACCEPTANCE;
347 	result->reason = 0;
348 	bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
349 
350 	/* sanity check */
351 	if (cont_list->n_context_elem != 1 ||
352 	    cont_list->p_cont_elem[0].n_transfer_syn != 1) {
353 		ndo_trace("ndr_svc_bind: warning: multiple p_cont_elem");
354 	}
355 
356 	p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
357 
358 	if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) != NULL) {
359 		/*
360 		 * Duplicate presentation context id.
361 		 */
362 		ndo_trace("ndr_svc_bind: duplicate binding");
363 		return (NDR_DRC_FAULT_BIND_PCONT_BUSY);
364 	}
365 
366 	if ((mbind = ndr_svc_new_binding(mxa)) == NULL) {
367 		/*
368 		 * No free binding slot
369 		 */
370 		result->result = NDR_PCDR_PROVIDER_REJECTION;
371 		result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED;
372 		ndo_trace("ndr_svc_bind: no resources");
373 		return (NDR_DRC_OK);
374 	}
375 
376 	as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
377 	as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
378 
379 	ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
380 	ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
381 
382 	msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers);
383 	if (msvc == NULL) {
384 		result->result = NDR_PCDR_PROVIDER_REJECTION;
385 		result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
386 		return (NDR_DRC_OK);
387 	}
388 
389 	/*
390 	 * We can now use the correct secondary address port.
391 	 */
392 	sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
393 	sec_addr->length = strlen(msvc->sec_addr_port) + 1;
394 	(void) strlcpy((char *)sec_addr->port_spec, msvc->sec_addr_port,
395 	    NDR_PORT_ANY_MAX_PORT_SPEC);
396 
397 	mbind->p_cont_id = p_cont_id;
398 	mbind->which_side = NDR_BIND_SIDE_SERVER;
399 	/* mbind->context set by app */
400 	mbind->service = msvc;
401 	mbind->instance_specific = 0;
402 
403 	mxa->binding = mbind;
404 
405 	if (msvc->bind_req) {
406 		/*
407 		 * Call the service-specific bind() handler.  If
408 		 * this fails, we shouild send a specific error
409 		 * on the bind ack.
410 		 */
411 		rc = (msvc->bind_req)(mxa);
412 		if (NDR_DRC_IS_FAULT(rc)) {
413 			mbind->service = 0;	/* free binding slot */
414 			mbind->which_side = 0;
415 			mbind->p_cont_id = 0;
416 			mbind->instance_specific = 0;
417 			return (rc);
418 		}
419 	}
420 
421 	result->transfer_syntax =
422 	    cont_list->p_cont_elem[0].transfer_syntaxes[0];
423 
424 	return (NDR_DRC_BINDING_MADE);
425 }
426 
427 /*
428  * ndr_svc_alter_context
429  *
430  * The alter context request is used to request additional presentation
431  * context for another interface and/or version.  It is very similar to
432  * a bind request.
433  */
434 static int
435 ndr_svc_alter_context(ndr_xa_t *mxa)
436 {
437 	ndr_p_result_list_t *result_list;
438 	ndr_p_result_t *result;
439 	ndr_p_cont_list_t *cont_list;
440 	ndr_binding_t *mbind;
441 	ndr_service_t *msvc;
442 	unsigned p_cont_id;
443 	ndr_uuid_t *as_uuid;
444 	ndr_uuid_t *ts_uuid;
445 	int as_vers;
446 	int ts_vers;
447 	ndr_port_any_t *sec_addr;
448 
449 	result_list = &mxa->send_hdr.alter_context_rsp_hdr.p_result_list;
450 	result_list->n_results = 1;
451 	result_list->reserved = 0;
452 	result_list->reserved2 = 0;
453 
454 	result = &result_list->p_results[0];
455 	result->result = NDR_PCDR_ACCEPTANCE;
456 	result->reason = 0;
457 	bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
458 
459 	cont_list = &mxa->recv_hdr.alter_context_hdr.p_context_elem;
460 	p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
461 
462 	if (ndr_svc_find_binding(mxa, p_cont_id) != NULL)
463 		return (NDR_DRC_FAULT_BIND_PCONT_BUSY);
464 
465 	if ((mbind = ndr_svc_new_binding(mxa)) == NULL) {
466 		result->result = NDR_PCDR_PROVIDER_REJECTION;
467 		result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED;
468 		return (NDR_DRC_OK);
469 	}
470 
471 	as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
472 	as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
473 
474 	ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
475 	ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
476 
477 	msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers);
478 	if (msvc == NULL) {
479 		result->result = NDR_PCDR_PROVIDER_REJECTION;
480 		result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
481 		return (NDR_DRC_OK);
482 	}
483 
484 	mbind->p_cont_id = p_cont_id;
485 	mbind->which_side = NDR_BIND_SIDE_SERVER;
486 	/* mbind->context set by app */
487 	mbind->service = msvc;
488 	mbind->instance_specific = 0;
489 	mxa->binding = mbind;
490 
491 	sec_addr = &mxa->send_hdr.alter_context_rsp_hdr.sec_addr;
492 	sec_addr->length = 0;
493 	bzero(sec_addr->port_spec, NDR_PORT_ANY_MAX_PORT_SPEC);
494 
495 	result->transfer_syntax =
496 	    cont_list->p_cont_elem[0].transfer_syntaxes[0];
497 
498 	return (NDR_DRC_BINDING_MADE);
499 }
500 
501 static int
502 ndr_svc_request(ndr_xa_t *mxa)
503 {
504 	ndr_binding_t	*mbind;
505 	ndr_service_t	*msvc;
506 	unsigned	p_cont_id;
507 	int		rc;
508 
509 	mxa->opnum = mxa->recv_hdr.request_hdr.opnum;
510 	p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id;
511 
512 	if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) == NULL)
513 		return (NDR_DRC_FAULT_REQUEST_PCONT_INVALID);
514 
515 	mxa->binding = mbind;
516 	msvc = mbind->service;
517 
518 	/*
519 	 * Make room for the response hdr.
520 	 */
521 	mxa->send_nds.pdu_scan_offset = NDR_RSP_HDR_SIZE;
522 
523 	if (msvc->call_stub)
524 		rc = (*msvc->call_stub)(mxa);
525 	else
526 		rc = ndr_generic_call_stub(mxa);
527 
528 	if (NDR_DRC_IS_FAULT(rc)) {
529 		ndo_printf(0, 0, "%s[0x%02x]: 0x%04x",
530 		    msvc->name, mxa->opnum, rc);
531 	}
532 
533 	return (rc);
534 }
535 
536 /*
537  * The transaction and the two nds streams use the same heap, which
538  * should already exist at this point.  The heap will also be available
539  * to the stub.
540  */
541 int
542 ndr_generic_call_stub(ndr_xa_t *mxa)
543 {
544 	ndr_binding_t 		*mbind = mxa->binding;
545 	ndr_service_t		*msvc = mbind->service;
546 	ndr_typeinfo_t		*intf_ti = msvc->interface_ti;
547 	ndr_stub_table_t	*ste;
548 	int			opnum = mxa->opnum;
549 	unsigned		p_len = intf_ti->c_size_fixed_part;
550 	char 			*param;
551 	int			rc;
552 
553 	if (mxa->heap == NULL) {
554 		ndo_printf(0, 0, "%s[0x%02x]: no heap", msvc->name, opnum);
555 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
556 	}
557 
558 	if ((ste = ndr_svc_find_stub(msvc, opnum)) == NULL) {
559 		ndo_printf(0, 0, "%s[0x%02x]: invalid opnum",
560 		    msvc->name, opnum);
561 		return (NDR_DRC_FAULT_REQUEST_OPNUM_INVALID);
562 	}
563 
564 	if ((param = ndr_heap_malloc(mxa->heap, p_len)) == NULL)
565 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
566 
567 	bzero(param, p_len);
568 
569 	rc = ndr_decode_call(mxa, param);
570 	if (!NDR_DRC_IS_OK(rc))
571 		return (rc);
572 
573 	rc = (*ste->func)(param, mxa);
574 	if (rc == NDR_DRC_OK)
575 		rc = ndr_encode_return(mxa, param);
576 
577 	return (rc);
578 }
579 
580 /*
581  * We can perform some initial setup of the response header here.
582  * We also need to cache some of the information from the bind
583  * negotiation for use during subsequent RPC calls.
584  */
585 static void
586 ndr_reply_prepare_hdr(ndr_xa_t *mxa)
587 {
588 	ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
589 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
590 
591 	hdr->rpc_vers = 5;
592 	hdr->rpc_vers_minor = 0;
593 	hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
594 	hdr->packed_drep = rhdr->packed_drep;
595 	hdr->frag_length = 0;
596 	hdr->auth_length = 0;
597 	hdr->call_id = rhdr->call_id;
598 #ifdef _BIG_ENDIAN
599 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
600 	    | NDR_REPLAB_INTG_BIG_ENDIAN;
601 #else
602 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
603 	    | NDR_REPLAB_INTG_LITTLE_ENDIAN;
604 #endif
605 
606 	switch (mxa->ptype) {
607 	case NDR_PTYPE_BIND:
608 		/*
609 		 * Compute the maximum fragment sizes for xmit/recv
610 		 * and store in the pipe endpoint.  Note "xmit" is
611 		 * client-to-server; "recv" is server-to-client.
612 		 */
613 		if (mxa->pipe->np_max_xmit_frag >
614 		    mxa->recv_hdr.bind_hdr.max_xmit_frag)
615 			mxa->pipe->np_max_xmit_frag =
616 			    mxa->recv_hdr.bind_hdr.max_xmit_frag;
617 		if (mxa->pipe->np_max_recv_frag >
618 		    mxa->recv_hdr.bind_hdr.max_recv_frag)
619 			mxa->pipe->np_max_recv_frag =
620 			    mxa->recv_hdr.bind_hdr.max_recv_frag;
621 
622 		hdr->ptype = NDR_PTYPE_BIND_ACK;
623 		mxa->send_hdr.bind_ack_hdr.max_xmit_frag =
624 		    mxa->pipe->np_max_xmit_frag;
625 		mxa->send_hdr.bind_ack_hdr.max_recv_frag =
626 		    mxa->pipe->np_max_recv_frag;
627 
628 		/*
629 		 * We're supposed to assign a unique "assoc group"
630 		 * (identifies this connection for the client).
631 		 * Using the pipe address is adequate.
632 		 */
633 		mxa->send_hdr.bind_ack_hdr.assoc_group_id =
634 		    mxa->recv_hdr.bind_hdr.assoc_group_id;
635 		if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0)
636 			mxa->send_hdr.bind_ack_hdr.assoc_group_id =
637 			    (DWORD)(uintptr_t)mxa->pipe;
638 
639 		break;
640 
641 	case NDR_PTYPE_REQUEST:
642 		hdr->ptype = NDR_PTYPE_RESPONSE;
643 		/* mxa->send_hdr.response_hdr.alloc_hint */
644 		mxa->send_hdr.response_hdr.p_cont_id =
645 		    mxa->recv_hdr.request_hdr.p_cont_id;
646 		mxa->send_hdr.response_hdr.cancel_count = 0;
647 		mxa->send_hdr.response_hdr.reserved = 0;
648 		break;
649 
650 	case NDR_PTYPE_ALTER_CONTEXT:
651 		hdr->ptype = NDR_PTYPE_ALTER_CONTEXT_RESP;
652 		/*
653 		 * The max_xmit_frag, max_recv_frag and assoc_group_id are
654 		 * ignored by the client but it's useful to fill them in.
655 		 */
656 		mxa->send_hdr.alter_context_rsp_hdr.max_xmit_frag =
657 		    mxa->recv_hdr.alter_context_hdr.max_xmit_frag;
658 		mxa->send_hdr.alter_context_rsp_hdr.max_recv_frag =
659 		    mxa->recv_hdr.alter_context_hdr.max_recv_frag;
660 		mxa->send_hdr.alter_context_rsp_hdr.assoc_group_id =
661 		    mxa->recv_hdr.alter_context_hdr.assoc_group_id;
662 		break;
663 
664 	default:
665 		hdr->ptype = 0xFF;
666 	}
667 }
668 
669 /*
670  * Signal an RPC fault. The stream is reset and we overwrite whatever
671  * was in the response header with the fault information.
672  */
673 static void
674 ndr_reply_fault(ndr_xa_t *mxa, unsigned long drc)
675 {
676 	ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
677 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
678 	ndr_stream_t *nds = &mxa->send_nds;
679 	unsigned long fault_status;
680 
681 	NDS_RESET(nds);
682 
683 	hdr->rpc_vers = 5;
684 	hdr->rpc_vers_minor = 0;
685 	hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
686 	hdr->packed_drep = rhdr->packed_drep;
687 	hdr->frag_length = sizeof (mxa->send_hdr.fault_hdr);
688 	hdr->auth_length = 0;
689 	hdr->call_id = rhdr->call_id;
690 #ifdef _BIG_ENDIAN
691 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
692 	    | NDR_REPLAB_INTG_BIG_ENDIAN;
693 #else
694 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
695 	    | NDR_REPLAB_INTG_LITTLE_ENDIAN;
696 #endif
697 
698 	switch (drc & NDR_DRC_MASK_SPECIFIER) {
699 	case NDR_DRC_FAULT_OUT_OF_MEMORY:
700 	case NDR_DRC_FAULT_ENCODE_TOO_BIG:
701 		fault_status = NDR_FAULT_NCA_OUT_ARGS_TOO_BIG;
702 		break;
703 
704 	case NDR_DRC_FAULT_REQUEST_PCONT_INVALID:
705 		fault_status = NDR_FAULT_NCA_INVALID_PRES_CONTEXT_ID;
706 		break;
707 
708 	case NDR_DRC_FAULT_REQUEST_OPNUM_INVALID:
709 		fault_status = NDR_FAULT_NCA_OP_RNG_ERROR;
710 		break;
711 
712 	case NDR_DRC_FAULT_DECODE_FAILED:
713 	case NDR_DRC_FAULT_ENCODE_FAILED:
714 		fault_status = NDR_FAULT_NCA_PROTO_ERROR;
715 		break;
716 
717 	default:
718 		fault_status = NDR_FAULT_NCA_UNSPEC_REJECT;
719 		break;
720 	}
721 
722 	mxa->send_hdr.fault_hdr.common_hdr.ptype = NDR_PTYPE_FAULT;
723 	mxa->send_hdr.fault_hdr.status = fault_status;
724 	mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length;
725 }
726 
727 /*
728  * Note that the frag_length for bind ack and alter context is
729  * non-standard.
730  */
731 static int
732 ndr_send_reply(ndr_xa_t *mxa)
733 {
734 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
735 	ndr_stream_t *nds = &mxa->send_nds;
736 	uint8_t *pdu_buf;
737 	unsigned long pdu_size;
738 	unsigned long frag_size;
739 	unsigned long pdu_data_size;
740 	unsigned long frag_data_size;
741 
742 	frag_size = mxa->pipe->np_max_recv_frag;
743 	pdu_size = nds->pdu_size;
744 	pdu_buf = nds->pdu_base_addr;
745 
746 	if (pdu_size <= frag_size) {
747 		/*
748 		 * Single fragment response. The PDU size may be zero
749 		 * here (i.e. bind or fault response). So don't make
750 		 * any assumptions about it until after the header is
751 		 * encoded.
752 		 */
753 		switch (hdr->ptype) {
754 		case NDR_PTYPE_BIND_ACK:
755 			hdr->frag_length = ndr_bind_ack_hdr_size(mxa);
756 			break;
757 
758 		case NDR_PTYPE_FAULT:
759 			/* already setup */
760 			break;
761 
762 		case NDR_PTYPE_RESPONSE:
763 			hdr->frag_length = pdu_size;
764 			mxa->send_hdr.response_hdr.alloc_hint =
765 			    hdr->frag_length;
766 			break;
767 
768 		case NDR_PTYPE_ALTER_CONTEXT_RESP:
769 			hdr->frag_length = ndr_alter_context_rsp_hdr_size();
770 			break;
771 
772 		default:
773 			hdr->frag_length = pdu_size;
774 			break;
775 		}
776 
777 		nds->pdu_scan_offset = 0;
778 		(void) ndr_encode_pdu_hdr(mxa);
779 		pdu_size = nds->pdu_size;
780 		(void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, pdu_size);
781 		return (0);
782 	}
783 
784 	/*
785 	 * Multiple fragment response.
786 	 *
787 	 * We need to update the RPC header for every fragment.
788 	 *
789 	 * pdu_data_size:	total data remaining to be handled
790 	 * frag_size:		total fragment size including header
791 	 * frag_data_size:	data in fragment
792 	 *			(i.e. frag_size - NDR_RSP_HDR_SIZE)
793 	 */
794 	pdu_data_size = pdu_size - NDR_RSP_HDR_SIZE;
795 	frag_data_size = frag_size - NDR_RSP_HDR_SIZE;
796 
797 	/*
798 	 * Send the first frag.
799 	 */
800 	hdr->pfc_flags = NDR_PFC_FIRST_FRAG;
801 	hdr->frag_length = frag_size;
802 	mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size;
803 	nds->pdu_scan_offset = 0;
804 	(void) ndr_encode_pdu_hdr(mxa);
805 	(void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size);
806 	pdu_data_size -= frag_data_size;
807 	pdu_buf += frag_data_size;
808 
809 	/*
810 	 * Send "middle" (full-sized) fragments...
811 	 */
812 	hdr->pfc_flags = 0;
813 	while (pdu_data_size > frag_data_size) {
814 
815 		hdr->frag_length = frag_size;
816 		mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size;
817 		nds->pdu_scan_offset = 0;
818 		(void) ndr_encode_pdu_hdr(mxa);
819 		bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE);
820 		(void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size);
821 		pdu_data_size -= frag_data_size;
822 		pdu_buf += frag_data_size;
823 	}
824 
825 	/*
826 	 * Last frag (pdu_data_size <= frag_data_size)
827 	 */
828 	hdr->pfc_flags = NDR_PFC_LAST_FRAG;
829 	frag_size = pdu_data_size + NDR_RSP_HDR_SIZE;
830 	hdr->frag_length = frag_size;
831 	mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size;
832 	nds->pdu_scan_offset = 0;
833 	(void) ndr_encode_pdu_hdr(mxa);
834 	bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE);
835 	(void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size);
836 
837 	return (0);
838 }
839