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