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