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