xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_opipe.c (revision 3b13a1ef7511135ec0c75b5f94de8075454efd79)
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 2011 Nexenta Systems, Inc.  All rights reserved.
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * This module provides the interface to NDR RPC.
28  */
29 
30 #include <sys/stat.h>
31 #include <sys/door.h>
32 #include <sys/door_data.h>
33 #include <sys/uio.h>
34 #include <sys/ksynch.h>
35 #include <smbsrv/smb_kproto.h>
36 #include <smbsrv/smb_xdr.h>
37 
38 #define	SMB_OPIPE_ISOPEN(OPIPE)	\
39 	(((OPIPE)->p_hdr.dh_magic == SMB_OPIPE_HDR_MAGIC) && \
40 	((OPIPE)->p_hdr.dh_fid))
41 
42 extern volatile uint32_t smb_fids;
43 
44 static int smb_opipe_do_open(smb_request_t *, smb_opipe_t *);
45 static char *smb_opipe_lookup(const char *);
46 static int smb_opipe_sethdr(smb_opipe_t *, uint32_t, uint32_t);
47 static int smb_opipe_exec(smb_opipe_t *);
48 static void smb_opipe_enter(smb_opipe_t *);
49 static void smb_opipe_exit(smb_opipe_t *);
50 
51 static door_handle_t smb_opipe_door_hd = NULL;
52 static int smb_opipe_door_id = -1;
53 static uint64_t smb_opipe_door_ncall = 0;
54 static kmutex_t smb_opipe_door_mutex;
55 static kcondvar_t smb_opipe_door_cv;
56 
57 static int smb_opipe_door_call(smb_opipe_t *);
58 static int smb_opipe_door_upcall(smb_opipe_t *);
59 
60 smb_opipe_t *
61 smb_opipe_alloc(smb_server_t *sv)
62 {
63 	smb_opipe_t	*opipe;
64 
65 	opipe = kmem_cache_alloc(sv->si_cache_opipe, KM_SLEEP);
66 
67 	bzero(opipe, sizeof (smb_opipe_t));
68 	mutex_init(&opipe->p_mutex, NULL, MUTEX_DEFAULT, NULL);
69 	cv_init(&opipe->p_cv, NULL, CV_DEFAULT, NULL);
70 	opipe->p_magic = SMB_OPIPE_MAGIC;
71 	opipe->p_server = sv;
72 
73 	smb_llist_enter(&sv->sv_opipe_list, RW_WRITER);
74 	smb_llist_insert_tail(&sv->sv_opipe_list, opipe);
75 	smb_llist_exit(&sv->sv_opipe_list);
76 
77 	return (opipe);
78 }
79 
80 void
81 smb_opipe_dealloc(smb_opipe_t *opipe)
82 {
83 	smb_server_t *sv;
84 
85 	SMB_OPIPE_VALID(opipe);
86 	sv = opipe->p_server;
87 	SMB_SERVER_VALID(sv);
88 
89 	smb_llist_enter(&sv->sv_opipe_list, RW_WRITER);
90 	smb_llist_remove(&sv->sv_opipe_list, opipe);
91 	smb_llist_exit(&sv->sv_opipe_list);
92 
93 	opipe->p_magic = (uint32_t)~SMB_OPIPE_MAGIC;
94 	smb_event_destroy(opipe->p_event);
95 	cv_destroy(&opipe->p_cv);
96 	mutex_destroy(&opipe->p_mutex);
97 
98 	kmem_cache_free(sv->si_cache_opipe, opipe);
99 }
100 
101 /*
102  * smb_opipe_open
103  *
104  * Open a well-known RPC named pipe. This routine should be called if
105  * a file open is requested on a share of type STYPE_IPC.
106  * If we recognize the pipe, we setup a new ofile.
107  *
108  * Returns 0 on success, Otherwise an NT status is returned to indicate
109  * an error.
110  */
111 int
112 smb_opipe_open(smb_request_t *sr)
113 {
114 	smb_arg_open_t	*op = &sr->sr_open;
115 	smb_ofile_t *of;
116 	smb_opipe_t *opipe;
117 	smb_doorhdr_t hdr;
118 	smb_error_t err;
119 	char *pipe_name;
120 
121 	if ((pipe_name = smb_opipe_lookup(op->fqi.fq_path.pn_path)) == NULL)
122 		return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
123 
124 	/*
125 	 * If printing is disabled, pretend spoolss does not exist.
126 	 */
127 	if (sr->sr_server->sv_cfg.skc_print_enable == 0 &&
128 	    strcmp(pipe_name, "SPOOLSS") == 0)
129 		return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
130 
131 	op->create_options = 0;
132 
133 	of = smb_ofile_open(sr, NULL, sr->smb_pid, op, SMB_FTYPE_MESG_PIPE,
134 	    SMB_UNIQ_FID(), &err);
135 
136 	if (of == NULL)
137 		return (err.status);
138 
139 	if (!smb_tree_is_connected(sr->tid_tree)) {
140 		smb_ofile_close(of, 0);
141 		smb_ofile_release(of);
142 		return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
143 	}
144 
145 	op->dsize = 0x01000;
146 	op->dattr = FILE_ATTRIBUTE_NORMAL;
147 	op->ftype = SMB_FTYPE_MESG_PIPE;
148 	op->action_taken = SMB_OACT_LOCK | SMB_OACT_OPENED; /* 0x8001 */
149 	op->devstate = SMB_PIPE_READMODE_MESSAGE
150 	    | SMB_PIPE_TYPE_MESSAGE
151 	    | SMB_PIPE_UNLIMITED_INSTANCES; /* 0x05ff */
152 	op->fileid = of->f_fid;
153 
154 	sr->smb_fid = of->f_fid;
155 	sr->fid_ofile = of;
156 
157 	opipe = of->f_pipe;
158 	smb_opipe_enter(opipe);
159 
160 	opipe->p_server = of->f_server;
161 	opipe->p_name = pipe_name;
162 	opipe->p_doorbuf = kmem_zalloc(SMB_OPIPE_DOOR_BUFSIZE, KM_SLEEP);
163 
164 	/*
165 	 * p_data points to the offset within p_doorbuf at which
166 	 * data will be written or read.
167 	 */
168 	opipe->p_data = opipe->p_doorbuf + xdr_sizeof(smb_doorhdr_xdr, &hdr);
169 
170 	if (smb_opipe_do_open(sr, opipe) != 0) {
171 		/*
172 		 * On error, reset the header to clear the fid,
173 		 * which avoids confusion when smb_opipe_close() is
174 		 * called by smb_ofile_close().
175 		 */
176 		bzero(&opipe->p_hdr, sizeof (smb_doorhdr_t));
177 		kmem_free(opipe->p_doorbuf, SMB_OPIPE_DOOR_BUFSIZE);
178 		smb_opipe_exit(opipe);
179 		smb_ofile_close(of, 0);
180 		return (NT_STATUS_NO_MEMORY);
181 	}
182 	smb_opipe_exit(opipe);
183 	return (NT_STATUS_SUCCESS);
184 }
185 
186 /*
187  * smb_opipe_lookup
188  *
189  * Lookup a path to see if it's a well-known RPC named pipe that we support.
190  * The full pipe path will be in the form \\PIPE\\SERVICE.  The first part
191  * can be assumed, so all we need here are the service names.
192  *
193  * Returns a pointer to the pipe name (without any leading \'s) on success.
194  * Otherwise returns a null pointer.
195  */
196 static char *
197 smb_opipe_lookup(const char *path)
198 {
199 	static char *named_pipes[] = {
200 		"lsass",
201 		"LSARPC",
202 		"NETLOGON",
203 		"SAMR",
204 		"SPOOLSS",
205 		"SRVSVC",
206 		"SVCCTL",
207 		"WINREG",
208 		"WKSSVC",
209 		"EVENTLOG",
210 		"NETDFS"
211 	};
212 
213 	const char *name;
214 	int i;
215 
216 	if (path == NULL)
217 		return (NULL);
218 
219 	name = path;
220 	name += strspn(name, "\\");
221 	if (smb_strcasecmp(name, "PIPE", 4) == 0) {
222 		path += 4;
223 		name += strspn(name, "\\");
224 	}
225 
226 	for (i = 0; i < sizeof (named_pipes) / sizeof (named_pipes[0]); ++i) {
227 		if (smb_strcasecmp(name, named_pipes[i], 0) == 0)
228 			return (named_pipes[i]);
229 	}
230 
231 	return (NULL);
232 }
233 
234 /*
235  * Initialize the opipe header and context, and make the door call.
236  */
237 static int
238 smb_opipe_do_open(smb_request_t *sr, smb_opipe_t *opipe)
239 {
240 	smb_netuserinfo_t *userinfo = &opipe->p_user;
241 	smb_user_t *user = sr->uid_user;
242 	uint8_t *buf = opipe->p_doorbuf;
243 	uint32_t buflen = SMB_OPIPE_DOOR_BUFSIZE;
244 	uint32_t len;
245 
246 	if ((opipe->p_event = smb_event_create(SMB_EVENT_TIMEOUT)) == NULL)
247 		return (-1);
248 
249 	smb_user_netinfo_init(user, userinfo);
250 	len = xdr_sizeof(smb_netuserinfo_xdr, userinfo);
251 
252 	bzero(&opipe->p_hdr, sizeof (smb_doorhdr_t));
253 	opipe->p_hdr.dh_magic = SMB_OPIPE_HDR_MAGIC;
254 	opipe->p_hdr.dh_flags = SMB_DF_SYSSPACE;
255 	opipe->p_hdr.dh_fid = smb_event_txid(opipe->p_event);
256 
257 	if (smb_opipe_sethdr(opipe, SMB_OPIPE_OPEN, len) == -1)
258 		return (-1);
259 
260 	len = xdr_sizeof(smb_doorhdr_xdr, &opipe->p_hdr);
261 	buf += len;
262 	buflen -= len;
263 
264 	if (smb_netuserinfo_encode(userinfo, buf, buflen, NULL) == -1)
265 		return (-1);
266 
267 	return (smb_opipe_door_call(opipe));
268 }
269 
270 /*
271  * smb_opipe_close
272  *
273  * Called whenever an IPC file/pipe is closed.
274  */
275 void
276 smb_opipe_close(smb_ofile_t *of)
277 {
278 	smb_opipe_t *opipe;
279 
280 	ASSERT(of);
281 	ASSERT(of->f_ftype == SMB_FTYPE_MESG_PIPE);
282 
283 	opipe = of->f_pipe;
284 	SMB_OPIPE_VALID(opipe);
285 
286 	(void) smb_server_cancel_event(opipe->p_hdr.dh_fid);
287 	smb_opipe_enter(opipe);
288 
289 	if (SMB_OPIPE_ISOPEN(opipe)) {
290 		(void) smb_opipe_sethdr(opipe, SMB_OPIPE_CLOSE, 0);
291 		(void) smb_opipe_door_call(opipe);
292 		bzero(&opipe->p_hdr, sizeof (smb_doorhdr_t));
293 		kmem_free(opipe->p_doorbuf, SMB_OPIPE_DOOR_BUFSIZE);
294 	}
295 
296 	smb_user_netinfo_fini(&opipe->p_user);
297 	smb_opipe_exit(opipe);
298 }
299 
300 static int
301 smb_opipe_sethdr(smb_opipe_t *opipe, uint32_t cmd, uint32_t datalen)
302 {
303 	opipe->p_hdr.dh_op = cmd;
304 	opipe->p_hdr.dh_txid = opipe->p_hdr.dh_fid;
305 	opipe->p_hdr.dh_datalen = datalen;
306 	opipe->p_hdr.dh_resid = 0;
307 	opipe->p_hdr.dh_door_rc = EINVAL;
308 
309 	return (smb_doorhdr_encode(&opipe->p_hdr, opipe->p_doorbuf,
310 	    SMB_OPIPE_DOOR_BUFSIZE));
311 }
312 
313 /*
314  * smb_opipe_transact
315  *
316  * This is the entry point for RPC bind and request transactions.
317  * The fid is an arbitrary id used to associate RPC requests with a
318  * particular binding handle.
319  *
320  * If the data to be returned is larger than the client expects, we
321  * return as much as the client can handle and report a buffer overflow
322  * warning, which informs the client that we have more data to return.
323  * The residual data remains in the pipe until the client claims it or
324  * closes the pipe.
325  */
326 smb_sdrc_t
327 smb_opipe_transact(smb_request_t *sr, struct uio *uio)
328 {
329 	smb_xa_t *xa;
330 	smb_opipe_t *opipe;
331 	struct mbuf *mhead;
332 	int mdrcnt;
333 	int nbytes;
334 	int rc;
335 
336 	if ((rc = smb_opipe_write(sr, uio)) != 0) {
337 		if (rc == EBADF)
338 			smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
339 			    ERRDOS, ERROR_INVALID_HANDLE);
340 		else
341 			smbsr_error(sr, NT_STATUS_INTERNAL_ERROR,
342 			    ERRDOS, ERROR_INTERNAL_ERROR);
343 		return (SDRC_ERROR);
344 	}
345 
346 	opipe = sr->fid_ofile->f_pipe;
347 
348 	if ((rc = smb_opipe_exec(opipe)) != 0) {
349 		smbsr_error(sr, NT_STATUS_INTERNAL_ERROR,
350 		    ERRDOS, ERROR_INTERNAL_ERROR);
351 		return (SDRC_ERROR);
352 	}
353 
354 	xa = sr->r_xa;
355 	mdrcnt = xa->smb_mdrcnt;
356 	smb_opipe_enter(opipe);
357 
358 	if (smb_opipe_sethdr(opipe, SMB_OPIPE_READ, mdrcnt) == -1) {
359 		smb_opipe_exit(opipe);
360 		smbsr_error(sr, NT_STATUS_INTERNAL_ERROR,
361 		    ERRDOS, ERROR_INTERNAL_ERROR);
362 		return (SDRC_ERROR);
363 	}
364 
365 	rc = smb_opipe_door_call(opipe);
366 	nbytes = opipe->p_hdr.dh_datalen;
367 
368 	if (rc != 0) {
369 		smb_opipe_exit(opipe);
370 		smbsr_error(sr, NT_STATUS_INTERNAL_ERROR,
371 		    ERRDOS, ERROR_INTERNAL_ERROR);
372 		return (SDRC_ERROR);
373 	}
374 
375 	if (nbytes) {
376 		mhead = smb_mbuf_get(opipe->p_data, nbytes);
377 		xa->rep_data_mb.max_bytes = nbytes;
378 		MBC_ATTACH_MBUF(&xa->rep_data_mb, mhead);
379 	}
380 
381 	if (opipe->p_hdr.dh_resid) {
382 		/*
383 		 * The pipe contains more data than mdrcnt, warn the
384 		 * client that there is more data in the pipe.
385 		 * Typically, the client will call SmbReadX, which
386 		 * will call smb_opipe_read, to get the data.
387 		 */
388 		smbsr_warn(sr, NT_STATUS_BUFFER_OVERFLOW,
389 		    ERRDOS, ERROR_MORE_DATA);
390 	}
391 
392 	smb_opipe_exit(opipe);
393 	return (SDRC_SUCCESS);
394 }
395 
396 /*
397  * smb_opipe_write
398  *
399  * Write RPC request data to the pipe.  The client should call smb_opipe_read
400  * to complete the exchange and obtain the RPC response.
401  *
402  * Returns 0 on success or an errno on failure.
403  */
404 int
405 smb_opipe_write(smb_request_t *sr, struct uio *uio)
406 {
407 	smb_opipe_t *opipe;
408 	uint32_t buflen;
409 	uint32_t len;
410 	int rc;
411 
412 	ASSERT(sr->fid_ofile);
413 	ASSERT(sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE);
414 
415 	opipe = sr->fid_ofile->f_pipe;
416 	SMB_OPIPE_VALID(opipe);
417 	smb_opipe_enter(opipe);
418 
419 	if (!SMB_OPIPE_ISOPEN(opipe)) {
420 		smb_opipe_exit(opipe);
421 		return (EBADF);
422 	}
423 
424 	rc = smb_opipe_sethdr(opipe, SMB_OPIPE_WRITE, uio->uio_resid);
425 	len = xdr_sizeof(smb_doorhdr_xdr, &opipe->p_hdr);
426 	if (rc == -1 || len == 0) {
427 		smb_opipe_exit(opipe);
428 		return (ENOMEM);
429 	}
430 
431 	buflen = SMB_OPIPE_DOOR_BUFSIZE - len;
432 	(void) uiomove((caddr_t)opipe->p_data, buflen, UIO_WRITE, uio);
433 
434 	rc = smb_opipe_door_call(opipe);
435 
436 	smb_opipe_exit(opipe);
437 	return ((rc == 0) ? 0 : EIO);
438 }
439 
440 /*
441  * smb_opipe_read
442  *
443  * This interface may be called because smb_opipe_transact could not return
444  * all of the data in the original transaction or to form the second half
445  * of a transaction set up using smb_opipe_write.  Either way, we just need
446  * to read data from the pipe and return it.
447  *
448  * The response data is encoded into raw_data as required by the smb_read
449  * functions.  The uio_resid value indicates the number of bytes read.
450  */
451 int
452 smb_opipe_read(smb_request_t *sr, struct uio *uio)
453 {
454 	smb_opipe_t *opipe;
455 	struct mbuf *mhead;
456 	uint32_t nbytes;
457 	int rc;
458 
459 	ASSERT(sr->fid_ofile);
460 	ASSERT(sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE);
461 
462 	opipe = sr->fid_ofile->f_pipe;
463 	SMB_OPIPE_VALID(opipe);
464 
465 	if ((rc = smb_opipe_exec(opipe)) != 0)
466 		return (EIO);
467 
468 	smb_opipe_enter(opipe);
469 
470 	if (!SMB_OPIPE_ISOPEN(opipe)) {
471 		smb_opipe_exit(opipe);
472 		return (EBADF);
473 	}
474 
475 	if (smb_opipe_sethdr(opipe, SMB_OPIPE_READ, uio->uio_resid) == -1) {
476 		smb_opipe_exit(opipe);
477 		return (ENOMEM);
478 	}
479 
480 	rc = smb_opipe_door_call(opipe);
481 	nbytes = opipe->p_hdr.dh_datalen;
482 
483 	if (rc != 0 || nbytes > uio->uio_resid) {
484 		smb_opipe_exit(opipe);
485 		return (EIO);
486 	}
487 
488 	if (nbytes) {
489 		mhead = smb_mbuf_get(opipe->p_data, nbytes);
490 		MBC_SETUP(&sr->raw_data, nbytes);
491 		MBC_ATTACH_MBUF(&sr->raw_data, mhead);
492 		uio->uio_resid -= nbytes;
493 	}
494 
495 	smb_opipe_exit(opipe);
496 	return (rc);
497 }
498 
499 static int
500 smb_opipe_exec(smb_opipe_t *opipe)
501 {
502 	uint32_t	len;
503 	int		rc;
504 
505 	smb_opipe_enter(opipe);
506 
507 	rc = smb_opipe_sethdr(opipe, SMB_OPIPE_EXEC, 0);
508 	len = xdr_sizeof(smb_doorhdr_xdr, &opipe->p_hdr);
509 	if (rc == -1 || len == 0) {
510 		smb_opipe_exit(opipe);
511 		return (ENOMEM);
512 	}
513 
514 	if ((rc = smb_opipe_door_call(opipe)) == 0)
515 		rc = smb_event_wait(opipe->p_event);
516 
517 	smb_opipe_exit(opipe);
518 	return (rc);
519 }
520 
521 /*
522  * Named pipe I/O is serialized per fid to ensure that each request
523  * has exclusive opipe access for the duration of the request.
524  */
525 static void
526 smb_opipe_enter(smb_opipe_t *opipe)
527 {
528 	mutex_enter(&opipe->p_mutex);
529 
530 	while (opipe->p_busy)
531 		cv_wait(&opipe->p_cv, &opipe->p_mutex);
532 
533 	opipe->p_busy = 1;
534 	mutex_exit(&opipe->p_mutex);
535 }
536 
537 /*
538  * Exit busy state.  If we have exec'd an RPC, we may have
539  * to wait for notification that processing has completed.
540  */
541 static void
542 smb_opipe_exit(smb_opipe_t *opipe)
543 {
544 	mutex_enter(&opipe->p_mutex);
545 	opipe->p_busy = 0;
546 	cv_signal(&opipe->p_cv);
547 	mutex_exit(&opipe->p_mutex);
548 }
549 
550 /*
551  * opipe door client (to user space door server).
552  */
553 void
554 smb_opipe_door_init(void)
555 {
556 	mutex_init(&smb_opipe_door_mutex, NULL, MUTEX_DEFAULT, NULL);
557 	cv_init(&smb_opipe_door_cv, NULL, CV_DEFAULT, NULL);
558 }
559 
560 void
561 smb_opipe_door_fini(void)
562 {
563 	smb_opipe_door_close();
564 	cv_destroy(&smb_opipe_door_cv);
565 	mutex_destroy(&smb_opipe_door_mutex);
566 }
567 
568 /*
569  * Open the (user space) door.  If the door is already open,
570  * close it first because the door-id has probably changed.
571  */
572 int
573 smb_opipe_door_open(int door_id)
574 {
575 	smb_opipe_door_close();
576 
577 	mutex_enter(&smb_opipe_door_mutex);
578 	smb_opipe_door_ncall = 0;
579 
580 	if (smb_opipe_door_hd == NULL) {
581 		smb_opipe_door_id = door_id;
582 		smb_opipe_door_hd = door_ki_lookup(door_id);
583 	}
584 
585 	mutex_exit(&smb_opipe_door_mutex);
586 	return ((smb_opipe_door_hd == NULL)  ? -1 : 0);
587 }
588 
589 /*
590  * Close the (user space) door.
591  */
592 void
593 smb_opipe_door_close(void)
594 {
595 	mutex_enter(&smb_opipe_door_mutex);
596 
597 	if (smb_opipe_door_hd != NULL) {
598 		while (smb_opipe_door_ncall > 0)
599 			cv_wait(&smb_opipe_door_cv, &smb_opipe_door_mutex);
600 
601 		door_ki_rele(smb_opipe_door_hd);
602 		smb_opipe_door_hd = NULL;
603 	}
604 
605 	mutex_exit(&smb_opipe_door_mutex);
606 }
607 
608 /*
609  * opipe door call interface.
610  * Door serialization and call reference accounting is handled here.
611  */
612 static int
613 smb_opipe_door_call(smb_opipe_t *opipe)
614 {
615 	int rc;
616 
617 	mutex_enter(&smb_opipe_door_mutex);
618 
619 	if (smb_opipe_door_hd == NULL) {
620 		mutex_exit(&smb_opipe_door_mutex);
621 
622 		if (smb_opipe_door_open(smb_opipe_door_id) != 0)
623 			return (-1);
624 
625 		mutex_enter(&smb_opipe_door_mutex);
626 	}
627 
628 	++smb_opipe_door_ncall;
629 	mutex_exit(&smb_opipe_door_mutex);
630 
631 	rc = smb_opipe_door_upcall(opipe);
632 
633 	mutex_enter(&smb_opipe_door_mutex);
634 	if ((--smb_opipe_door_ncall) == 0)
635 		cv_signal(&smb_opipe_door_cv);
636 	mutex_exit(&smb_opipe_door_mutex);
637 	return (rc);
638 }
639 
640 /*
641  * Door upcall wrapper - handles data marshalling.
642  * This function should only be called by smb_opipe_door_call.
643  */
644 static int
645 smb_opipe_door_upcall(smb_opipe_t *opipe)
646 {
647 	door_arg_t da;
648 	smb_doorhdr_t hdr;
649 	int i;
650 	int rc;
651 
652 	da.data_ptr = (char *)opipe->p_doorbuf;
653 	da.data_size = SMB_OPIPE_DOOR_BUFSIZE;
654 	da.desc_ptr = NULL;
655 	da.desc_num = 0;
656 	da.rbuf = (char *)opipe->p_doorbuf;
657 	da.rsize = SMB_OPIPE_DOOR_BUFSIZE;
658 
659 	for (i = 0; i < 3; ++i) {
660 		if (smb_server_is_stopping())
661 			return (-1);
662 
663 		if ((rc = door_ki_upcall_limited(smb_opipe_door_hd, &da,
664 		    NULL, SIZE_MAX, 0)) == 0)
665 			break;
666 
667 		if (rc != EAGAIN && rc != EINTR)
668 			return (-1);
669 	}
670 
671 	/* Check for door_return(NULL, 0, NULL, 0) */
672 	if (rc != 0 || da.data_size == 0 || da.rsize == 0)
673 		return (-1);
674 
675 	if (smb_doorhdr_decode(&hdr, (uint8_t *)da.data_ptr, da.rsize) == -1)
676 		return (-1);
677 
678 	if ((hdr.dh_magic != SMB_OPIPE_HDR_MAGIC) ||
679 	    (hdr.dh_fid != opipe->p_hdr.dh_fid) ||
680 	    (hdr.dh_op != opipe->p_hdr.dh_op) ||
681 	    (hdr.dh_door_rc != 0) ||
682 	    (hdr.dh_datalen > SMB_OPIPE_DOOR_BUFSIZE)) {
683 		return (-1);
684 	}
685 
686 	opipe->p_hdr.dh_datalen = hdr.dh_datalen;
687 	opipe->p_hdr.dh_resid = hdr.dh_resid;
688 	return (0);
689 }
690