1/*
2 * Copyright (c) 2000-2001 Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *    This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $Id: smb_dev.c,v 1.21 2004/12/13 00:25:18 lindak Exp $
33 */
34
35/*
36 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
37 * Use is subject to license terms.
38 */
39
40#include <sys/types.h>
41#include <sys/param.h>
42#include <sys/errno.h>
43#include <sys/sysmacros.h>
44#include <sys/uio.h>
45#include <sys/buf.h>
46#include <sys/modctl.h>
47#include <sys/open.h>
48#include <sys/file.h>
49#include <sys/kmem.h>
50#include <sys/conf.h>
51#include <sys/cmn_err.h>
52#include <sys/stat.h>
53#include <sys/ddi.h>
54#include <sys/sunddi.h>
55#include <sys/sunldi.h>
56#include <sys/policy.h>
57#include <sys/zone.h>
58#include <sys/pathname.h>
59#include <sys/mount.h>
60#include <sys/sdt.h>
61#include <fs/fs_subr.h>
62#include <sys/modctl.h>
63#include <sys/devops.h>
64#include <sys/thread.h>
65#include <sys/mkdev.h>
66#include <sys/types.h>
67#include <sys/zone.h>
68
69#include <netsmb/smb_osdep.h>
70#include <netsmb/mchain.h>		/* for "htoles()" */
71
72#include <netsmb/smb.h>
73#include <netsmb/smb_conn.h>
74#include <netsmb/smb_subr.h>
75#include <netsmb/smb_dev.h>
76#include <netsmb/smb_pass.h>
77
78/* for version checks */
79const uint32_t nsmb_version = NSMB_VERSION;
80
81/*
82 * Userland code loops through minor #s 0 to 1023, looking for one which opens.
83 * Intially we create minor 0 and leave it for anyone.  Minor zero will never
84 * actually get used - opening triggers creation of another (but private) minor,
85 * which userland code will get to and mark busy.
86 */
87#define	SMBMINORS 1024
88static void *statep;
89static major_t nsmb_major;
90static minor_t nsmb_minor = 1;
91
92#define	NSMB_MAX_MINOR  (1 << 8)
93#define	NSMB_MIN_MINOR   (NSMB_MAX_MINOR + 1)
94
95#define	ILP32	1
96#define	LP64	2
97
98static kmutex_t  dev_lck;
99
100/* Zone support */
101zone_key_t nsmb_zone_key;
102extern void nsmb_zone_shutdown(zoneid_t zoneid, void *data);
103extern void nsmb_zone_destroy(zoneid_t zoneid, void *data);
104
105/*
106 * cb_ops device operations.
107 */
108static int nsmb_open(dev_t *devp, int flag, int otyp, cred_t *credp);
109static int nsmb_close(dev_t dev, int flag, int otyp, cred_t *credp);
110static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
111				cred_t *credp, int *rvalp);
112static int nsmb_close2(smb_dev_t *sdp, cred_t *cr);
113
114/* smbfs cb_ops */
115static struct cb_ops nsmb_cbops = {
116	nsmb_open,	/* open */
117	nsmb_close,	/* close */
118	nodev,		/* strategy */
119	nodev,		/* print */
120	nodev,		/* dump */
121	nodev,		/* read */
122	nodev,		/* write */
123	nsmb_ioctl,	/* ioctl */
124	nodev,		/* devmap */
125	nodev,		/* mmap */
126	nodev,		/* segmap */
127	nochpoll,	/* poll */
128	ddi_prop_op,	/* prop_op */
129	NULL,		/* stream */
130	D_MP,		/* cb_flag */
131	CB_REV,		/* rev */
132	nodev,		/* int (*cb_aread)() */
133	nodev		/* int (*cb_awrite)() */
134};
135
136/*
137 * Device options
138 */
139static int nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
140static int nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
141static int nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
142	void *arg, void **result);
143
144static struct dev_ops nsmb_ops = {
145	DEVO_REV,	/* devo_rev, */
146	0,		/* refcnt  */
147	nsmb_getinfo,	/* info */
148	nulldev,	/* identify */
149	nulldev,	/* probe */
150	nsmb_attach,	/* attach */
151	nsmb_detach,	/* detach */
152	nodev,		/* reset */
153	&nsmb_cbops,	/* driver ops - devctl interfaces */
154	NULL,		/* bus operations */
155	NULL,		/* power */
156	ddi_quiesce_not_needed,	/* quiesce */
157};
158
159/*
160 * Module linkage information.
161 */
162
163static struct modldrv nsmb_modldrv = {
164	&mod_driverops,				/* Driver module */
165	"SMBFS network driver",
166	&nsmb_ops				/* Driver ops */
167};
168
169static struct modlinkage nsmb_modlinkage = {
170	MODREV_1,
171	(void *)&nsmb_modldrv,
172	NULL
173};
174
175int
176_init(void)
177{
178	int error;
179
180	(void) ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1);
181
182	/* Can initialize some mutexes also. */
183	mutex_init(&dev_lck, NULL, MUTEX_DRIVER, NULL);
184	/*
185	 * Create a major name and number.
186	 */
187	nsmb_major = ddi_name_to_major(NSMB_NAME);
188	nsmb_minor = 0;
189
190	/* Connection data structures. */
191	(void) smb_sm_init();
192
193	/* Initialize password Key chain DB. */
194	smb_pkey_init();
195
196	/* Time conversion stuff. */
197	smb_time_init();
198
199	/* Initialize crypto mechanisms. */
200	smb_crypto_mech_init();
201
202	zone_key_create(&nsmb_zone_key, NULL, nsmb_zone_shutdown,
203	    nsmb_zone_destroy);
204
205	/*
206	 * Install the module.  Do this after other init,
207	 * to prevent entrances before we're ready.
208	 */
209	if ((error = mod_install((&nsmb_modlinkage))) != 0) {
210
211		/* Same as 2nd half of _fini */
212		(void) zone_key_delete(nsmb_zone_key);
213		smb_pkey_fini();
214		smb_sm_done();
215		mutex_destroy(&dev_lck);
216		ddi_soft_state_fini(&statep);
217
218		return (error);
219	}
220
221	return (0);
222}
223
224int
225_fini(void)
226{
227	int status;
228
229	/*
230	 * Prevent unload if we have active VCs
231	 * or stored passwords
232	 */
233	if ((status = smb_sm_idle()) != 0)
234		return (status);
235	if ((status = smb_pkey_idle()) != 0)
236		return (status);
237
238	/*
239	 * Remove the module.  Do this before destroying things,
240	 * to prevent new entrances while we're destorying.
241	 */
242	if ((status = mod_remove(&nsmb_modlinkage)) != 0) {
243		return (status);
244	}
245
246	(void) zone_key_delete(nsmb_zone_key);
247
248	/* Time conversion stuff. */
249	smb_time_fini();
250
251	/* Destroy password Key chain DB. */
252	smb_pkey_fini();
253
254	smb_sm_done();
255
256	mutex_destroy(&dev_lck);
257	ddi_soft_state_fini(&statep);
258
259	return (status);
260}
261
262int
263_info(struct modinfo *modinfop)
264{
265	return (mod_info(&nsmb_modlinkage, modinfop));
266}
267
268/*ARGSUSED*/
269static int
270nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
271{
272	int ret = DDI_SUCCESS;
273
274	switch (cmd) {
275	case DDI_INFO_DEVT2DEVINFO:
276		*result = 0;
277		break;
278	case DDI_INFO_DEVT2INSTANCE:
279		*result = 0;
280		break;
281	default:
282		ret = DDI_FAILURE;
283	}
284	return (ret);
285}
286
287static int
288nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
289{
290	smb_dev_t *sdp;
291
292	if (cmd != DDI_ATTACH)
293		return (DDI_FAILURE);
294	/*
295	 * only one instance - but we clone using the open routine
296	 */
297	if (ddi_get_instance(dip) > 0)
298		return (DDI_FAILURE);
299
300	mutex_enter(&dev_lck);
301
302	/*
303	 * This is the Zero'th minor device which is created.
304	 */
305	if (ddi_soft_state_zalloc(statep, 0) == DDI_FAILURE) {
306		cmn_err(CE_WARN, "nsmb_attach: soft state alloc");
307		goto attach_failed;
308	}
309	if (ddi_create_minor_node(dip, "nsmb", S_IFCHR, 0, DDI_PSEUDO,
310	    NULL) == DDI_FAILURE) {
311		cmn_err(CE_WARN, "nsmb_attach: create minor");
312		goto attach_failed;
313	}
314	if ((sdp = ddi_get_soft_state(statep, 0)) == NULL) {
315		cmn_err(CE_WARN, "nsmb_attach: get soft state");
316		ddi_remove_minor_node(dip, NULL);
317		goto attach_failed;
318	}
319
320	/*
321	 * Need to see if this field is required.
322	 * REVISIT
323	 */
324	sdp->smb_dip = dip;
325	sdp->sd_seq = 0;
326	sdp->sd_opened = 1;
327
328	mutex_exit(&dev_lck);
329	ddi_report_dev(dip);
330	return (DDI_SUCCESS);
331
332attach_failed:
333	ddi_soft_state_free(statep, 0);
334	mutex_exit(&dev_lck);
335	return (DDI_FAILURE);
336}
337
338/*ARGSUSED*/
339static int
340nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
341{
342
343	if (cmd != DDI_DETACH)
344		return (DDI_FAILURE);
345	if (ddi_get_instance(dip) > 0)
346		return (DDI_FAILURE);
347
348	ddi_soft_state_free(statep, 0);
349	ddi_remove_minor_node(dip, NULL);
350
351	return (DDI_SUCCESS);
352}
353
354/*ARGSUSED*/
355static int
356nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int flags,	/* model.h */
357	cred_t *cr, int *rvalp)
358{
359	smb_dev_t *sdp;
360	int err;
361
362	sdp = ddi_get_soft_state(statep, getminor(dev));
363	if (sdp == NULL) {
364		return (DDI_FAILURE);
365	}
366	if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
367		return (EBADF);
368	}
369
370	/*
371	 * Dont give access if the zone id is not as the same as we
372	 * set in the nsmb_open or dont belong to the global zone.
373	 * Check if the user belongs to this zone..
374	 */
375	if (sdp->zoneid != getzoneid())
376		return (EIO);
377
378	/*
379	 * We have a zone_shutdown call back that kills all the VCs
380	 * in a zone that's shutting down.  That action will cause
381	 * all of these ioctls to fail on such VCs, so no need to
382	 * check the zone status here on every ioctl call.
383	 */
384
385	err = 0;
386	switch (cmd) {
387	case SMBIOC_GETVERS:
388		(void) ddi_copyout(&nsmb_version, (void *)arg,
389		    sizeof (nsmb_version), flags);
390		break;
391
392	case SMBIOC_FLAGS2:
393		err = smb_usr_get_flags2(sdp, arg, flags);
394		break;
395
396	case SMBIOC_GETSSNKEY:
397		err = smb_usr_get_ssnkey(sdp, arg, flags);
398		break;
399
400	case SMBIOC_REQUEST:
401		err = smb_usr_simplerq(sdp, arg, flags, cr);
402		break;
403
404	case SMBIOC_T2RQ:
405		err = smb_usr_t2request(sdp, arg, flags, cr);
406		break;
407
408	case SMBIOC_READ:
409	case SMBIOC_WRITE:
410		err = smb_usr_rw(sdp, cmd, arg, flags, cr);
411		break;
412
413	case SMBIOC_SSN_CREATE:
414	case SMBIOC_SSN_FIND:
415		err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr);
416		break;
417
418	case SMBIOC_SSN_KILL:
419	case SMBIOC_SSN_RELE:
420		err = smb_usr_drop_ssn(sdp, cmd);
421		break;
422
423	case SMBIOC_TREE_CONNECT:
424	case SMBIOC_TREE_FIND:
425		err = smb_usr_get_tree(sdp, cmd, arg, flags, cr);
426		break;
427
428	case SMBIOC_TREE_KILL:
429	case SMBIOC_TREE_RELE:
430		err = smb_usr_drop_tree(sdp, cmd);
431		break;
432
433	case SMBIOC_IOD_WORK:
434		err = smb_usr_iod_work(sdp, arg, flags, cr);
435		break;
436
437	case SMBIOC_IOD_IDLE:
438	case SMBIOC_IOD_RCFAIL:
439		err = smb_usr_iod_ioctl(sdp, cmd, arg, flags);
440		break;
441
442	case SMBIOC_PK_ADD:
443	case SMBIOC_PK_DEL:
444	case SMBIOC_PK_CHK:
445	case SMBIOC_PK_DEL_OWNER:
446	case SMBIOC_PK_DEL_EVERYONE:
447		err = smb_pkey_ioctl(cmd, arg, flags, cr);
448		break;
449
450	default:
451		err = ENOTTY;
452		break;
453	}
454
455	return (err);
456}
457
458/*ARGSUSED*/
459static int
460nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr)
461{
462	major_t new_major;
463	smb_dev_t *sdp, *sdv;
464
465	mutex_enter(&dev_lck);
466	for (; ; ) {
467		minor_t start = nsmb_minor;
468		do {
469			if (nsmb_minor >= MAXMIN32) {
470				if (nsmb_major == getmajor(*dev))
471					nsmb_minor = NSMB_MIN_MINOR;
472				else
473					nsmb_minor = 0;
474			} else {
475				nsmb_minor++;
476			}
477			sdv = ddi_get_soft_state(statep, nsmb_minor);
478		} while ((sdv != NULL) && (nsmb_minor != start));
479		if (nsmb_minor == start) {
480			/*
481			 * The condition we need to solve here is  all the
482			 * MAXMIN32(~262000) minors numbers are reached. We
483			 * need to create a new major number.
484			 * zfs uses getudev() to create a new major number.
485			 */
486			if ((new_major = getudev()) == (major_t)-1) {
487				cmn_err(CE_WARN,
488				    "nsmb: Can't get unique major "
489				    "device number.");
490				mutex_exit(&dev_lck);
491				return (-1);
492			}
493			nsmb_major = new_major;
494			nsmb_minor = 0;
495		} else {
496			break;
497		}
498	}
499
500	/*
501	 * This is called by mount or open call.
502	 * The open() routine is passed a pointer to a device number so
503	 * that  the  driver  can  change the minor number. This allows
504	 * drivers to dynamically  create minor instances of  the  dev-
505	 * ice.  An  example of this might be a  pseudo-terminal driver
506	 * that creates a new pseudo-terminal whenever it   is  opened.
507	 * A driver that chooses the minor number dynamically, normally
508	 * creates only one  minor  device  node  in   attach(9E)  with
509	 * ddi_create_minor_node(9F) then changes the minor number com-
510	 * ponent of *devp using makedevice(9F)  and  getmajor(9F)  The
511	 * driver needs to keep track of available minor numbers inter-
512	 * nally.
513	 * Stuff the structure smb_dev.
514	 * return.
515	 */
516
517	if (ddi_soft_state_zalloc(statep, nsmb_minor) == DDI_FAILURE) {
518		mutex_exit(&dev_lck);
519		return (ENXIO);
520	}
521	if ((sdp = ddi_get_soft_state(statep, nsmb_minor)) == NULL) {
522		mutex_exit(&dev_lck);
523		return (ENXIO);
524	}
525
526	sdp->sd_opened = 1;
527	sdp->sd_seq = nsmb_minor;
528	sdp->smb_cred = cr;
529	sdp->sd_flags |= NSMBFL_OPEN;
530	sdp->zoneid = crgetzoneid(cr);
531	mutex_exit(&dev_lck);
532
533	*dev = makedevice(nsmb_major, nsmb_minor);
534
535	return (0);
536}
537
538/*ARGSUSED*/
539static int
540nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr)
541{
542	minor_t inst = getminor(dev);
543	smb_dev_t *sdp;
544	int err;
545
546	mutex_enter(&dev_lck);
547	/*
548	 * 1. Check the validity of the minor number.
549	 * 2. Release any shares/vc associated  with the connection.
550	 * 3. Can close the minor number.
551	 * 4. Deallocate any resources allocated in open() call.
552	 */
553
554	sdp = ddi_get_soft_state(statep, inst);
555	if (sdp != NULL)
556		err = nsmb_close2(sdp, cr);
557	else
558		err = ENXIO;
559
560	/*
561	 * Free the instance
562	 */
563	ddi_soft_state_free(statep, inst);
564	mutex_exit(&dev_lck);
565	return (err);
566}
567
568static int
569nsmb_close2(smb_dev_t *sdp, cred_t *cr)
570{
571	struct smb_vc *vcp;
572	struct smb_share *ssp;
573	struct smb_cred scred;
574
575	smb_credinit(&scred, cr);
576	ssp = sdp->sd_share;
577	if (ssp != NULL)
578		smb_share_rele(ssp);
579	vcp = sdp->sd_vc;
580	if (vcp != NULL) {
581		/*
582		 * If this dev minor was opened by smbiod,
583		 * mark this VC as "dead" because it now
584		 * will have no IOD to service it.
585		 */
586		if (sdp->sd_flags & NSMBFL_IOD)
587			smb_iod_disconnect(vcp);
588		smb_vc_rele(vcp);
589	}
590
591	smb_credrele(&scred);
592	return (0);
593}
594
595int
596smb_dev2share(int fd, struct smb_share **sspp)
597{
598	file_t *fp = NULL;
599	vnode_t *vp;
600	smb_dev_t *sdp;
601	smb_share_t *ssp;
602	dev_t dev;
603	int err;
604
605	if ((fp = getf(fd)) == NULL)
606		return (EBADF);
607
608	vp = fp->f_vnode;
609	dev = vp->v_rdev;
610	if (dev == 0 || dev == NODEV ||
611	    getmajor(dev) != nsmb_major) {
612		err = EBADF;
613		goto out;
614	}
615
616	sdp = ddi_get_soft_state(statep, getminor(dev));
617	if (sdp == NULL) {
618		err = EINVAL;
619		goto out;
620	}
621
622	ssp = sdp->sd_share;
623	if (ssp == NULL) {
624		err = ENOTCONN;
625		goto out;
626	}
627
628	/*
629	 * Our caller gains a ref. to this share.
630	 */
631	*sspp = ssp;
632	smb_share_hold(ssp);
633	err = 0;
634
635out:
636	if (fp)
637		releasef(fd);
638	return (err);
639}
640