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