xref: /titanic_51/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c (revision 53a7b6b6763f5865522a76e5e887390a8f4777d7)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Server side RPC handler.
28  */
29 
30 #include <sys/byteorder.h>
31 #include <sys/errno.h>
32 #include <sys/uio.h>
33 #include <thread.h>
34 #include <synch.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <string.h>
38 #include <time.h>
39 
40 #include <smbsrv/libsmb.h>
41 #include <smbsrv/libmlrpc.h>
42 
43 /*
44  * Fragment size (5680: NT style).
45  */
46 #define	NDR_FRAG_SZ		5680
47 
48 #define	NDR_PIPE_BUFSZ		65536
49 #define	NDR_PIPE_MAX		128
50 static ndr_pipe_t ndr_pipe_table[NDR_PIPE_MAX];
51 static mutex_t ndr_pipe_lock;
52 
53 static int ndr_pipe_transact(ndr_pipe_t *);
54 static ndr_pipe_t *ndr_pipe_lookup(int);
55 static void ndr_pipe_release(ndr_pipe_t *);
56 static ndr_pipe_t *ndr_pipe_allocate(int);
57 static void ndr_pipe_deallocate(ndr_pipe_t *);
58 static void ndr_pipe_rewind(ndr_pipe_t *);
59 static void ndr_pipe_flush(ndr_pipe_t *);
60 
61 static int ndr_svc_process(ndr_xa_t *);
62 static int ndr_svc_bind(ndr_xa_t *);
63 static int ndr_svc_request(ndr_xa_t *);
64 static void ndr_reply_prepare_hdr(ndr_xa_t *);
65 static int ndr_svc_alter_context(ndr_xa_t *);
66 static void ndr_reply_fault(ndr_xa_t *, unsigned long);
67 static int ndr_build_reply(ndr_xa_t *);
68 static void ndr_build_frag(ndr_stream_t *, uint8_t *, uint32_t);
69 
70 /*
71  * Allocate and associate a service context with a fid.
72  */
73 int
74 ndr_pipe_open(int fid, uint8_t *data, uint32_t datalen)
75 {
76 	ndr_pipe_t *np;
77 
78 	(void) mutex_lock(&ndr_pipe_lock);
79 
80 	if ((np = ndr_pipe_lookup(fid)) != NULL) {
81 		ndr_pipe_release(np);
82 		(void) mutex_unlock(&ndr_pipe_lock);
83 		return (EEXIST);
84 	}
85 
86 	if ((np = ndr_pipe_allocate(fid)) == NULL) {
87 		(void) mutex_unlock(&ndr_pipe_lock);
88 		return (ENOMEM);
89 	}
90 
91 	if (smb_opipe_context_decode(&np->np_ctx, data, datalen) == -1) {
92 		ndr_pipe_release(np);
93 		(void) mutex_unlock(&ndr_pipe_lock);
94 		return (EINVAL);
95 	}
96 
97 	ndr_svc_binding_pool_init(&np->np_binding, np->np_binding_pool,
98 	    NDR_N_BINDING_POOL);
99 
100 	(void) mutex_unlock(&ndr_pipe_lock);
101 	return (0);
102 }
103 
104 /*
105  * Release the context associated with a fid when an opipe is closed.
106  */
107 int
108 ndr_pipe_close(int fid)
109 {
110 	ndr_pipe_t *np;
111 
112 	(void) mutex_lock(&ndr_pipe_lock);
113 
114 	if ((np = ndr_pipe_lookup(fid)) == NULL) {
115 		(void) mutex_unlock(&ndr_pipe_lock);
116 		return (ENOENT);
117 	}
118 
119 	/*
120 	 * Release twice: once for the lookup above
121 	 * and again to close the fid.
122 	 */
123 	ndr_pipe_release(np);
124 	ndr_pipe_release(np);
125 	(void) mutex_unlock(&ndr_pipe_lock);
126 	return (0);
127 }
128 
129 /*
130  * Write RPC request data to the input stream.  Input data is buffered
131  * until the response is requested.
132  */
133 int
134 ndr_pipe_write(int fid, uint8_t *buf, uint32_t len)
135 {
136 	ndr_pipe_t *np;
137 	ssize_t nbytes;
138 
139 	if (len == 0)
140 		return (0);
141 
142 	(void) mutex_lock(&ndr_pipe_lock);
143 
144 	if ((np = ndr_pipe_lookup(fid)) == NULL) {
145 		(void) mutex_unlock(&ndr_pipe_lock);
146 		return (ENOENT);
147 	}
148 
149 	nbytes = ndr_uiomove((caddr_t)buf, len, UIO_READ, &np->np_uio);
150 
151 	ndr_pipe_release(np);
152 	(void) mutex_unlock(&ndr_pipe_lock);
153 	return ((nbytes == len) ? 0 : EIO);
154 }
155 
156 /*
157  * Read RPC response data.  If the input stream contains an RPC request,
158  * we need to process the RPC transaction, which will place the RPC
159  * response in the output (frags) stream.  Otherwise, read data from
160  * the output stream.
161  */
162 int
163 ndr_pipe_read(int fid, uint8_t *buf, uint32_t *len, uint32_t *resid)
164 {
165 	ndr_pipe_t *np;
166 	ssize_t nbytes = *len;
167 	int rc;
168 
169 	if (nbytes == 0) {
170 		*resid = 0;
171 		return (0);
172 	}
173 
174 	(void) mutex_lock(&ndr_pipe_lock);
175 	if ((np = ndr_pipe_lookup(fid)) == NULL) {
176 		(void) mutex_unlock(&ndr_pipe_lock);
177 		return (ENOENT);
178 	}
179 	(void) mutex_unlock(&ndr_pipe_lock);
180 
181 	if (np->np_uio.uio_offset) {
182 		if ((rc = ndr_pipe_transact(np)) != 0) {
183 			ndr_pipe_flush(np);
184 			(void) mutex_lock(&ndr_pipe_lock);
185 			ndr_pipe_release(np);
186 			(void) mutex_unlock(&ndr_pipe_lock);
187 			return (rc);
188 		}
189 
190 	}
191 
192 	*len = ndr_uiomove((caddr_t)buf, nbytes, UIO_WRITE, &np->np_frags.uio);
193 	*resid = np->np_frags.uio.uio_resid;
194 
195 	if (*resid == 0) {
196 		/*
197 		 * Nothing left, cleanup the output stream.
198 		 */
199 		ndr_pipe_flush(np);
200 	}
201 
202 	(void) mutex_lock(&ndr_pipe_lock);
203 	ndr_pipe_release(np);
204 	(void) mutex_unlock(&ndr_pipe_lock);
205 	return (0);
206 }
207 
208 /*
209  * Process a server-side RPC request.
210  */
211 static int
212 ndr_pipe_transact(ndr_pipe_t *np)
213 {
214 	ndr_xa_t	*mxa;
215 	ndr_stream_t	*recv_nds;
216 	ndr_stream_t	*send_nds;
217 	char		*data;
218 	int		datalen;
219 
220 	data = np->np_buf;
221 	datalen = np->np_uio.uio_offset;
222 
223 	if ((mxa = (ndr_xa_t *)malloc(sizeof (ndr_xa_t))) == NULL)
224 		return (ENOMEM);
225 
226 	bzero(mxa, sizeof (ndr_xa_t));
227 	mxa->fid = np->np_fid;
228 	mxa->pipe = np;
229 	mxa->binding_list = np->np_binding;
230 
231 	if ((mxa->heap = ndr_heap_create()) == NULL) {
232 		free(mxa);
233 		return (ENOMEM);
234 	}
235 
236 	recv_nds = &mxa->recv_nds;
237 	nds_initialize(recv_nds, datalen, NDR_MODE_CALL_RECV, mxa->heap);
238 
239 	/*
240 	 * Copy the input data and reset the input stream.
241 	 */
242 	bcopy(data, recv_nds->pdu_base_addr, datalen);
243 	ndr_pipe_rewind(np);
244 
245 	send_nds = &mxa->send_nds;
246 	nds_initialize(send_nds, 0, NDR_MODE_RETURN_SEND, mxa->heap);
247 
248 	(void) ndr_svc_process(mxa);
249 
250 	nds_finalize(send_nds, &np->np_frags);
251 	nds_destruct(&mxa->recv_nds);
252 	nds_destruct(&mxa->send_nds);
253 	ndr_heap_destroy(mxa->heap);
254 	free(mxa);
255 	return (0);
256 }
257 
258 /*
259  * Must be called with ndr_pipe_lock held.
260  */
261 static ndr_pipe_t *
262 ndr_pipe_lookup(int fid)
263 {
264 	ndr_pipe_t *np;
265 	int i;
266 
267 	for (i = 0; i < NDR_PIPE_MAX; ++i) {
268 		np = &ndr_pipe_table[i];
269 
270 		if (np->np_fid == fid) {
271 			if (np->np_refcnt == 0)
272 				return (NULL);
273 
274 			np->np_refcnt++;
275 			return (np);
276 		}
277 	}
278 
279 	return (NULL);
280 }
281 
282 /*
283  * Must be called with ndr_pipe_lock held.
284  */
285 static void
286 ndr_pipe_release(ndr_pipe_t *np)
287 {
288 	np->np_refcnt--;
289 	ndr_pipe_deallocate(np);
290 }
291 
292 /*
293  * Must be called with ndr_pipe_lock held.
294  */
295 static ndr_pipe_t *
296 ndr_pipe_allocate(int fid)
297 {
298 	ndr_pipe_t *np = NULL;
299 	int i;
300 
301 	for (i = 0; i < NDR_PIPE_MAX; ++i) {
302 		np = &ndr_pipe_table[i];
303 
304 		if (np->np_fid == 0) {
305 			bzero(np, sizeof (ndr_pipe_t));
306 
307 			if ((np->np_buf = malloc(NDR_PIPE_BUFSZ)) == NULL)
308 				return (NULL);
309 
310 			ndr_pipe_rewind(np);
311 			np->np_fid = fid;
312 			np->np_refcnt = 1;
313 			return (np);
314 		}
315 	}
316 
317 	return (NULL);
318 }
319 
320 /*
321  * Must be called with ndr_pipe_lock held.
322  */
323 static void
324 ndr_pipe_deallocate(ndr_pipe_t *np)
325 {
326 	if (np->np_refcnt == 0) {
327 		/*
328 		 * Ensure that there are no RPC service policy handles
329 		 * (associated with this fid) left around.
330 		 */
331 		ndr_hdclose(np->np_fid);
332 
333 		ndr_pipe_rewind(np);
334 		ndr_pipe_flush(np);
335 		free(np->np_buf);
336 		free(np->np_ctx.oc_domain);
337 		free(np->np_ctx.oc_account);
338 		free(np->np_ctx.oc_workstation);
339 		bzero(np, sizeof (ndr_pipe_t));
340 	}
341 }
342 
343 /*
344  * Rewind the input data stream, ready for the next write.
345  */
346 static void
347 ndr_pipe_rewind(ndr_pipe_t *np)
348 {
349 	np->np_uio.uio_iov = &np->np_iov;
350 	np->np_uio.uio_iovcnt = 1;
351 	np->np_uio.uio_offset = 0;
352 	np->np_uio.uio_segflg = UIO_USERSPACE;
353 	np->np_uio.uio_resid = NDR_PIPE_BUFSZ;
354 	np->np_iov.iov_base = np->np_buf;
355 	np->np_iov.iov_len = NDR_PIPE_BUFSZ;
356 }
357 
358 /*
359  * Flush the output data stream.
360  */
361 static void
362 ndr_pipe_flush(ndr_pipe_t *np)
363 {
364 	ndr_frag_t *frag;
365 
366 	while ((frag = np->np_frags.head) != NULL) {
367 		np->np_frags.head = frag->next;
368 		free(frag);
369 	}
370 
371 	free(np->np_frags.iov);
372 	bzero(&np->np_frags, sizeof (ndr_fraglist_t));
373 }
374 
375 /*
376  * Check whether or not the specified user has administrator privileges,
377  * i.e. is a member of Domain Admins or Administrators.
378  * Returns true if the user is an administrator, otherwise returns false.
379  */
380 boolean_t
381 ndr_is_admin(ndr_xa_t *xa)
382 {
383 	smb_opipe_context_t *ctx = &xa->pipe->np_ctx;
384 
385 	return (ctx->oc_flags & SMB_ATF_ADMIN);
386 }
387 
388 /*
389  * Check whether or not the specified user has power-user privileges,
390  * i.e. is a member of Domain Admins, Administrators or Power Users.
391  * This is typically required for operations such as managing shares.
392  * Returns true if the user is a power user, otherwise returns false.
393  */
394 boolean_t
395 ndr_is_poweruser(ndr_xa_t *xa)
396 {
397 	smb_opipe_context_t *ctx = &xa->pipe->np_ctx;
398 
399 	return ((ctx->oc_flags & SMB_ATF_ADMIN) ||
400 	    (ctx->oc_flags & SMB_ATF_POWERUSER));
401 }
402 
403 int32_t
404 ndr_native_os(ndr_xa_t *xa)
405 {
406 	smb_opipe_context_t *ctx = &xa->pipe->np_ctx;
407 
408 	return (ctx->oc_native_os);
409 }
410 
411 /*
412  * This is the entry point for all server-side RPC processing.
413  * It is assumed that the PDU has already been received.
414  */
415 static int
416 ndr_svc_process(ndr_xa_t *mxa)
417 {
418 	int rc;
419 
420 	rc = ndr_decode_pdu_hdr(mxa);
421 	if (!NDR_DRC_IS_OK(rc))
422 		return (-1);
423 
424 	(void) ndr_reply_prepare_hdr(mxa);
425 
426 	switch (mxa->ptype) {
427 	case NDR_PTYPE_BIND:
428 		rc = ndr_svc_bind(mxa);
429 		break;
430 
431 	case NDR_PTYPE_REQUEST:
432 		rc = ndr_svc_request(mxa);
433 		break;
434 
435 	case NDR_PTYPE_ALTER_CONTEXT:
436 		rc = ndr_svc_alter_context(mxa);
437 		break;
438 
439 	default:
440 		rc = NDR_DRC_FAULT_RPCHDR_PTYPE_INVALID;
441 		break;
442 	}
443 
444 	if (NDR_DRC_IS_FAULT(rc))
445 		ndr_reply_fault(mxa, rc);
446 
447 	(void) ndr_build_reply(mxa);
448 	return (rc);
449 }
450 
451 /*
452  * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple
453  * p_results[] not supported.
454  */
455 static int
456 ndr_svc_bind(ndr_xa_t *mxa)
457 {
458 	ndr_p_cont_list_t	*cont_list;
459 	ndr_p_result_list_t	*result_list;
460 	ndr_p_result_t		*result;
461 	unsigned		p_cont_id;
462 	ndr_binding_t		*mbind;
463 	ndr_uuid_t		*as_uuid;
464 	ndr_uuid_t		*ts_uuid;
465 	int			as_vers;
466 	int			ts_vers;
467 	ndr_service_t		*msvc;
468 	int			rc;
469 	ndr_port_any_t		*sec_addr;
470 
471 	/* acquire targets */
472 	cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem;
473 	result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list;
474 	result = &result_list->p_results[0];
475 
476 	/*
477 	 * Set up temporary secondary address port.
478 	 * We will correct this later (below).
479 	 */
480 	sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
481 	sec_addr->length = 13;
482 	(void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs");
483 
484 	result_list->n_results = 1;
485 	result_list->reserved = 0;
486 	result_list->reserved2 = 0;
487 	result->result = NDR_PCDR_ACCEPTANCE;
488 	result->reason = 0;
489 	bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
490 
491 	/* sanity check */
492 	if (cont_list->n_context_elem != 1 ||
493 	    cont_list->p_cont_elem[0].n_transfer_syn != 1) {
494 		ndo_trace("ndr_svc_bind: warning: multiple p_cont_elem");
495 	}
496 
497 	p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
498 
499 	if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) != NULL) {
500 		/*
501 		 * Duplicate presentation context id.
502 		 */
503 		ndo_trace("ndr_svc_bind: duplicate binding");
504 		return (NDR_DRC_FAULT_BIND_PCONT_BUSY);
505 	}
506 
507 	if ((mbind = ndr_svc_new_binding(mxa)) == NULL) {
508 		/*
509 		 * No free binding slot
510 		 */
511 		result->result = NDR_PCDR_PROVIDER_REJECTION;
512 		result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED;
513 		ndo_trace("ndr_svc_bind: no resources");
514 		return (NDR_DRC_OK);
515 	}
516 
517 	as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
518 	as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
519 
520 	ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
521 	ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
522 
523 	msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers);
524 	if (msvc == NULL) {
525 		result->result = NDR_PCDR_PROVIDER_REJECTION;
526 		result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
527 		return (NDR_DRC_OK);
528 	}
529 
530 	/*
531 	 * We can now use the correct secondary address port.
532 	 */
533 	sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
534 	sec_addr->length = strlen(msvc->sec_addr_port) + 1;
535 	(void) strlcpy((char *)sec_addr->port_spec, msvc->sec_addr_port,
536 	    NDR_PORT_ANY_MAX_PORT_SPEC);
537 
538 	mbind->p_cont_id = p_cont_id;
539 	mbind->which_side = NDR_BIND_SIDE_SERVER;
540 	/* mbind->context set by app */
541 	mbind->service = msvc;
542 	mbind->instance_specific = 0;
543 
544 	mxa->binding = mbind;
545 
546 	if (msvc->bind_req) {
547 		/*
548 		 * Call the service-specific bind() handler.  If
549 		 * this fails, we shouild send a specific error
550 		 * on the bind ack.
551 		 */
552 		rc = (msvc->bind_req)(mxa);
553 		if (NDR_DRC_IS_FAULT(rc)) {
554 			mbind->service = 0;	/* free binding slot */
555 			mbind->which_side = 0;
556 			mbind->p_cont_id = 0;
557 			mbind->instance_specific = 0;
558 			return (rc);
559 		}
560 	}
561 
562 	result->transfer_syntax =
563 	    cont_list->p_cont_elem[0].transfer_syntaxes[0];
564 
565 	return (NDR_DRC_BINDING_MADE);
566 }
567 
568 /*
569  * ndr_svc_alter_context
570  *
571  * The alter context request is used to request additional presentation
572  * context for another interface and/or version.  It is very similar to
573  * a bind request.
574  */
575 static int
576 ndr_svc_alter_context(ndr_xa_t *mxa)
577 {
578 	ndr_p_result_list_t *result_list;
579 	ndr_p_result_t *result;
580 	ndr_p_cont_list_t *cont_list;
581 	ndr_binding_t *mbind;
582 	ndr_service_t *msvc;
583 	unsigned p_cont_id;
584 	ndr_uuid_t *as_uuid;
585 	ndr_uuid_t *ts_uuid;
586 	int as_vers;
587 	int ts_vers;
588 	ndr_port_any_t *sec_addr;
589 
590 	result_list = &mxa->send_hdr.alter_context_rsp_hdr.p_result_list;
591 	result_list->n_results = 1;
592 	result_list->reserved = 0;
593 	result_list->reserved2 = 0;
594 
595 	result = &result_list->p_results[0];
596 	result->result = NDR_PCDR_ACCEPTANCE;
597 	result->reason = 0;
598 	bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
599 
600 	cont_list = &mxa->recv_hdr.alter_context_hdr.p_context_elem;
601 	p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
602 
603 	if (ndr_svc_find_binding(mxa, p_cont_id) != NULL)
604 		return (NDR_DRC_FAULT_BIND_PCONT_BUSY);
605 
606 	if ((mbind = ndr_svc_new_binding(mxa)) == NULL) {
607 		result->result = NDR_PCDR_PROVIDER_REJECTION;
608 		result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED;
609 		return (NDR_DRC_OK);
610 	}
611 
612 	as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
613 	as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
614 
615 	ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
616 	ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
617 
618 	msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers);
619 	if (msvc == NULL) {
620 		result->result = NDR_PCDR_PROVIDER_REJECTION;
621 		result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
622 		return (NDR_DRC_OK);
623 	}
624 
625 	mbind->p_cont_id = p_cont_id;
626 	mbind->which_side = NDR_BIND_SIDE_SERVER;
627 	/* mbind->context set by app */
628 	mbind->service = msvc;
629 	mbind->instance_specific = 0;
630 	mxa->binding = mbind;
631 
632 	sec_addr = &mxa->send_hdr.alter_context_rsp_hdr.sec_addr;
633 	sec_addr->length = 0;
634 	bzero(sec_addr->port_spec, NDR_PORT_ANY_MAX_PORT_SPEC);
635 
636 	result->transfer_syntax =
637 	    cont_list->p_cont_elem[0].transfer_syntaxes[0];
638 
639 	return (NDR_DRC_BINDING_MADE);
640 }
641 
642 static int
643 ndr_svc_request(ndr_xa_t *mxa)
644 {
645 	ndr_binding_t	*mbind;
646 	ndr_service_t	*msvc;
647 	unsigned	p_cont_id;
648 	int		rc;
649 
650 	mxa->opnum = mxa->recv_hdr.request_hdr.opnum;
651 	p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id;
652 
653 	if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) == NULL)
654 		return (NDR_DRC_FAULT_REQUEST_PCONT_INVALID);
655 
656 	mxa->binding = mbind;
657 	msvc = mbind->service;
658 
659 	/*
660 	 * Make room for the response hdr.
661 	 */
662 	mxa->send_nds.pdu_scan_offset = NDR_RSP_HDR_SIZE;
663 
664 	if (msvc->call_stub)
665 		rc = (*msvc->call_stub)(mxa);
666 	else
667 		rc = ndr_generic_call_stub(mxa);
668 
669 	if (NDR_DRC_IS_FAULT(rc)) {
670 		ndo_printf(0, 0, "%s[0x%02x]: 0x%04x",
671 		    msvc->name, mxa->opnum, rc);
672 	}
673 
674 	return (rc);
675 }
676 
677 /*
678  * The transaction and the two nds streams use the same heap, which
679  * should already exist at this point.  The heap will also be available
680  * to the stub.
681  */
682 int
683 ndr_generic_call_stub(ndr_xa_t *mxa)
684 {
685 	ndr_binding_t 		*mbind = mxa->binding;
686 	ndr_service_t		*msvc = mbind->service;
687 	ndr_typeinfo_t		*intf_ti = msvc->interface_ti;
688 	ndr_stub_table_t	*ste;
689 	int			opnum = mxa->opnum;
690 	unsigned		p_len = intf_ti->c_size_fixed_part;
691 	char 			*param;
692 	int			rc;
693 
694 	if (mxa->heap == NULL) {
695 		ndo_printf(0, 0, "%s[0x%02x]: no heap", msvc->name, opnum);
696 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
697 	}
698 
699 	if ((ste = ndr_svc_find_stub(msvc, opnum)) == NULL) {
700 		ndo_printf(0, 0, "%s[0x%02x]: invalid opnum",
701 		    msvc->name, opnum);
702 		return (NDR_DRC_FAULT_REQUEST_OPNUM_INVALID);
703 	}
704 
705 	if ((param = ndr_heap_malloc(mxa->heap, p_len)) == NULL)
706 		return (NDR_DRC_FAULT_OUT_OF_MEMORY);
707 
708 	bzero(param, p_len);
709 
710 	rc = ndr_decode_call(mxa, param);
711 	if (!NDR_DRC_IS_OK(rc))
712 		return (rc);
713 
714 	rc = (*ste->func)(param, mxa);
715 	if (rc == NDR_DRC_OK)
716 		rc = ndr_encode_return(mxa, param);
717 
718 	return (rc);
719 }
720 
721 /*
722  * We can perform some initial setup of the response header here.
723  * We also need to cache some of the information from the bind
724  * negotiation for use during subsequent RPC calls.
725  */
726 static void
727 ndr_reply_prepare_hdr(ndr_xa_t *mxa)
728 {
729 	ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
730 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
731 
732 	hdr->rpc_vers = 5;
733 	hdr->rpc_vers_minor = 0;
734 	hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
735 	hdr->packed_drep = rhdr->packed_drep;
736 	hdr->frag_length = 0;
737 	hdr->auth_length = 0;
738 	hdr->call_id = rhdr->call_id;
739 #ifdef _BIG_ENDIAN
740 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
741 	    | NDR_REPLAB_INTG_BIG_ENDIAN;
742 #else
743 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
744 	    | NDR_REPLAB_INTG_LITTLE_ENDIAN;
745 #endif
746 
747 	switch (mxa->ptype) {
748 	case NDR_PTYPE_BIND:
749 		hdr->ptype = NDR_PTYPE_BIND_ACK;
750 		mxa->send_hdr.bind_ack_hdr.max_xmit_frag =
751 		    mxa->recv_hdr.bind_hdr.max_xmit_frag;
752 		mxa->send_hdr.bind_ack_hdr.max_recv_frag =
753 		    mxa->recv_hdr.bind_hdr.max_recv_frag;
754 		mxa->send_hdr.bind_ack_hdr.assoc_group_id =
755 		    mxa->recv_hdr.bind_hdr.assoc_group_id;
756 
757 		if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0)
758 			mxa->send_hdr.bind_ack_hdr.assoc_group_id = time(0);
759 
760 		/*
761 		 * Save the maximum fragment sizes
762 		 * for use with subsequent requests.
763 		 */
764 		mxa->pipe->np_max_xmit_frag =
765 		    mxa->recv_hdr.bind_hdr.max_xmit_frag;
766 		mxa->pipe->np_max_recv_frag =
767 		    mxa->recv_hdr.bind_hdr.max_recv_frag;
768 		break;
769 
770 	case NDR_PTYPE_REQUEST:
771 		hdr->ptype = NDR_PTYPE_RESPONSE;
772 		/* mxa->send_hdr.response_hdr.alloc_hint */
773 		mxa->send_hdr.response_hdr.p_cont_id =
774 		    mxa->recv_hdr.request_hdr.p_cont_id;
775 		mxa->send_hdr.response_hdr.cancel_count = 0;
776 		mxa->send_hdr.response_hdr.reserved = 0;
777 		break;
778 
779 	case NDR_PTYPE_ALTER_CONTEXT:
780 		hdr->ptype = NDR_PTYPE_ALTER_CONTEXT_RESP;
781 		/*
782 		 * The max_xmit_frag, max_recv_frag and assoc_group_id are
783 		 * ignored by the client but it's useful to fill them in.
784 		 */
785 		mxa->send_hdr.alter_context_rsp_hdr.max_xmit_frag =
786 		    mxa->recv_hdr.alter_context_hdr.max_xmit_frag;
787 		mxa->send_hdr.alter_context_rsp_hdr.max_recv_frag =
788 		    mxa->recv_hdr.alter_context_hdr.max_recv_frag;
789 		mxa->send_hdr.alter_context_rsp_hdr.assoc_group_id =
790 		    mxa->recv_hdr.alter_context_hdr.assoc_group_id;
791 		break;
792 
793 	default:
794 		hdr->ptype = 0xFF;
795 	}
796 }
797 
798 /*
799  * Signal an RPC fault. The stream is reset and we overwrite whatever
800  * was in the response header with the fault information.
801  */
802 static void
803 ndr_reply_fault(ndr_xa_t *mxa, unsigned long drc)
804 {
805 	ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
806 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
807 	ndr_stream_t *nds = &mxa->send_nds;
808 	unsigned long fault_status;
809 
810 	NDS_RESET(nds);
811 
812 	hdr->rpc_vers = 5;
813 	hdr->rpc_vers_minor = 0;
814 	hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
815 	hdr->packed_drep = rhdr->packed_drep;
816 	hdr->frag_length = sizeof (mxa->send_hdr.fault_hdr);
817 	hdr->auth_length = 0;
818 	hdr->call_id = rhdr->call_id;
819 #ifdef _BIG_ENDIAN
820 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
821 	    | NDR_REPLAB_INTG_BIG_ENDIAN;
822 #else
823 	hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
824 	    | NDR_REPLAB_INTG_LITTLE_ENDIAN;
825 #endif
826 
827 	switch (drc & NDR_DRC_MASK_SPECIFIER) {
828 	case NDR_DRC_FAULT_OUT_OF_MEMORY:
829 	case NDR_DRC_FAULT_ENCODE_TOO_BIG:
830 		fault_status = NDR_FAULT_NCA_OUT_ARGS_TOO_BIG;
831 		break;
832 
833 	case NDR_DRC_FAULT_REQUEST_PCONT_INVALID:
834 		fault_status = NDR_FAULT_NCA_INVALID_PRES_CONTEXT_ID;
835 		break;
836 
837 	case NDR_DRC_FAULT_REQUEST_OPNUM_INVALID:
838 		fault_status = NDR_FAULT_NCA_OP_RNG_ERROR;
839 		break;
840 
841 	case NDR_DRC_FAULT_DECODE_FAILED:
842 	case NDR_DRC_FAULT_ENCODE_FAILED:
843 		fault_status = NDR_FAULT_NCA_PROTO_ERROR;
844 		break;
845 
846 	default:
847 		fault_status = NDR_FAULT_NCA_UNSPEC_REJECT;
848 		break;
849 	}
850 
851 	mxa->send_hdr.fault_hdr.common_hdr.ptype = NDR_PTYPE_FAULT;
852 	mxa->send_hdr.fault_hdr.status = fault_status;
853 	mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length;
854 }
855 
856 /*
857  * Note that the frag_length for bind ack and alter context is
858  * non-standard.
859  */
860 static int
861 ndr_build_reply(ndr_xa_t *mxa)
862 {
863 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
864 	ndr_stream_t *nds = &mxa->send_nds;
865 	uint8_t *pdu_buf;
866 	unsigned long pdu_size;
867 	unsigned long frag_size;
868 	unsigned long pdu_data_size;
869 	unsigned long frag_data_size;
870 
871 	frag_size = NDR_FRAG_SZ;
872 	pdu_size = nds->pdu_size;
873 	pdu_buf = nds->pdu_base_addr;
874 
875 	if (pdu_size <= frag_size) {
876 		/*
877 		 * Single fragment response. The PDU size may be zero
878 		 * here (i.e. bind or fault response). So don't make
879 		 * any assumptions about it until after the header is
880 		 * encoded.
881 		 */
882 		switch (hdr->ptype) {
883 		case NDR_PTYPE_BIND_ACK:
884 			hdr->frag_length = ndr_bind_ack_hdr_size(mxa);
885 			break;
886 
887 		case NDR_PTYPE_FAULT:
888 			/* already setup */
889 			break;
890 
891 		case NDR_PTYPE_RESPONSE:
892 			hdr->frag_length = pdu_size;
893 			mxa->send_hdr.response_hdr.alloc_hint =
894 			    hdr->frag_length;
895 			break;
896 
897 		case NDR_PTYPE_ALTER_CONTEXT_RESP:
898 			hdr->frag_length = ndr_alter_context_rsp_hdr_size();
899 			break;
900 
901 		default:
902 			hdr->frag_length = pdu_size;
903 			break;
904 		}
905 
906 		nds->pdu_scan_offset = 0;
907 		(void) ndr_encode_pdu_hdr(mxa);
908 		pdu_size = nds->pdu_size;
909 		ndr_build_frag(nds, pdu_buf,  pdu_size);
910 		return (0);
911 	}
912 
913 	/*
914 	 * Multiple fragment response.
915 	 */
916 	hdr->pfc_flags = NDR_PFC_FIRST_FRAG;
917 	hdr->frag_length = frag_size;
918 	mxa->send_hdr.response_hdr.alloc_hint = pdu_size - NDR_RSP_HDR_SIZE;
919 	nds->pdu_scan_offset = 0;
920 	(void) ndr_encode_pdu_hdr(mxa);
921 	ndr_build_frag(nds, pdu_buf,  frag_size);
922 
923 	/*
924 	 * We need to update the 24-byte header in subsequent fragments.
925 	 *
926 	 * pdu_data_size:	total data remaining to be handled
927 	 * frag_size:		total fragment size including header
928 	 * frag_data_size:	data in fragment
929 	 *			(i.e. frag_size - NDR_RSP_HDR_SIZE)
930 	 */
931 	pdu_data_size = pdu_size - NDR_RSP_HDR_SIZE;
932 	frag_data_size = frag_size - NDR_RSP_HDR_SIZE;
933 
934 	while (pdu_data_size) {
935 		mxa->send_hdr.response_hdr.alloc_hint -= frag_data_size;
936 		pdu_data_size -= frag_data_size;
937 		pdu_buf += frag_data_size;
938 
939 		if (pdu_data_size <= frag_data_size) {
940 			frag_data_size = pdu_data_size;
941 			frag_size = frag_data_size + NDR_RSP_HDR_SIZE;
942 			hdr->pfc_flags = NDR_PFC_LAST_FRAG;
943 		} else {
944 			hdr->pfc_flags = 0;
945 		}
946 
947 		hdr->frag_length = frag_size;
948 		nds->pdu_scan_offset = 0;
949 		(void) ndr_encode_pdu_hdr(mxa);
950 		bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE);
951 
952 		ndr_build_frag(nds, pdu_buf, frag_size);
953 
954 		if (hdr->pfc_flags & NDR_PFC_LAST_FRAG)
955 			break;
956 	}
957 
958 	return (0);
959 }
960 
961 /*
962  * ndr_build_frag
963  *
964  * Build an RPC PDU fragment from the specified buffer.
965  * If malloc fails, the client will see a header/pdu inconsistency
966  * and report an error.
967  */
968 static void
969 ndr_build_frag(ndr_stream_t *nds, uint8_t *buf, uint32_t len)
970 {
971 	ndr_frag_t *frag;
972 	int size = sizeof (ndr_frag_t) + len;
973 
974 	if ((frag = (ndr_frag_t *)malloc(size)) == NULL)
975 		return;
976 
977 	frag->next = NULL;
978 	frag->buf = (uint8_t *)frag + sizeof (ndr_frag_t);
979 	frag->len = len;
980 	bcopy(buf, frag->buf, len);
981 
982 	if (nds->frags.head == NULL) {
983 		nds->frags.head = frag;
984 		nds->frags.tail = frag;
985 		nds->frags.nfrag = 1;
986 	} else {
987 		nds->frags.tail->next = frag;
988 		nds->frags.tail = frag;
989 		++nds->frags.nfrag;
990 	}
991 }
992