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