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
33 /*
34 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
35 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
36 * Use is subject to license terms.
37 */
38
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/errno.h>
42 #include <sys/sysmacros.h>
43 #include <sys/uio.h>
44 #include <sys/buf.h>
45 #include <sys/modctl.h>
46 #include <sys/open.h>
47 #include <sys/file.h>
48 #include <sys/kmem.h>
49 #include <sys/conf.h>
50 #include <sys/cmn_err.h>
51 #include <sys/stat.h>
52 #include <sys/ddi.h>
53 #include <sys/sunddi.h>
54 #include <sys/sunldi.h>
55 #include <sys/policy.h>
56 #include <sys/zone.h>
57 #include <sys/pathname.h>
58 #include <sys/mount.h>
59 #include <sys/sdt.h>
60 #include <fs/fs_subr.h>
61 #include <sys/modctl.h>
62 #include <sys/devops.h>
63 #include <sys/thread.h>
64 #include <sys/types.h>
65 #include <sys/zone.h>
66
67 #include <netsmb/smb_osdep.h>
68 #include <netsmb/mchain.h> /* for "htoles()" */
69
70 #include <netsmb/smb.h>
71 #include <netsmb/smb_conn.h>
72 #include <netsmb/smb_subr.h>
73 #include <netsmb/smb_dev.h>
74 #include <netsmb/smb_pass.h>
75
76 #define NSMB_MIN_MINOR 1
77 #define NSMB_MAX_MINOR L_MAXMIN32
78
79 /* for version checks */
80 const uint32_t nsmb_version = NSMB_VERSION;
81
82 static void *statep;
83 static major_t nsmb_major;
84 static minor_t last_minor = NSMB_MIN_MINOR;
85 static dev_info_t *nsmb_dip;
86 static kmutex_t dev_lck;
87
88 /* Zone support */
89 zone_key_t nsmb_zone_key;
90 extern void nsmb_zone_shutdown(zoneid_t zoneid, void *data);
91 extern void nsmb_zone_destroy(zoneid_t zoneid, void *data);
92
93 /*
94 * cb_ops device operations.
95 */
96 static int nsmb_open(dev_t *devp, int flag, int otyp, cred_t *credp);
97 static int nsmb_close(dev_t dev, int flag, int otyp, cred_t *credp);
98 static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
99 cred_t *credp, int *rvalp);
100 static int nsmb_close2(smb_dev_t *sdp, cred_t *cr);
101
102 /* smbfs cb_ops */
103 static struct cb_ops nsmb_cbops = {
104 nsmb_open, /* open */
105 nsmb_close, /* close */
106 nodev, /* strategy */
107 nodev, /* print */
108 nodev, /* dump */
109 nodev, /* read */
110 nodev, /* write */
111 nsmb_ioctl, /* ioctl */
112 nodev, /* devmap */
113 nodev, /* mmap */
114 nodev, /* segmap */
115 nochpoll, /* poll */
116 ddi_prop_op, /* prop_op */
117 NULL, /* stream */
118 D_MP, /* cb_flag */
119 CB_REV, /* rev */
120 nodev, /* int (*cb_aread)() */
121 nodev /* int (*cb_awrite)() */
122 };
123
124 /*
125 * Device options
126 */
127 static int nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
128 static int nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
129 static int nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
130 void *arg, void **result);
131
132 static struct dev_ops nsmb_ops = {
133 DEVO_REV, /* devo_rev, */
134 0, /* refcnt */
135 nsmb_getinfo, /* info */
136 nulldev, /* identify */
137 nulldev, /* probe */
138 nsmb_attach, /* attach */
139 nsmb_detach, /* detach */
140 nodev, /* reset */
141 &nsmb_cbops, /* driver ops - devctl interfaces */
142 NULL, /* bus operations */
143 NULL, /* power */
144 ddi_quiesce_not_needed, /* quiesce */
145 };
146
147 /*
148 * Module linkage information.
149 */
150
151 static struct modldrv nsmb_modldrv = {
152 &mod_driverops, /* Driver module */
153 "SMBFS network driver",
154 &nsmb_ops /* Driver ops */
155 };
156
157 static struct modlinkage nsmb_modlinkage = {
158 MODREV_1,
159 (void *)&nsmb_modldrv,
160 NULL
161 };
162
163 int
_init(void)164 _init(void)
165 {
166 int error;
167
168 (void) ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1);
169
170 /* Can initialize some mutexes also. */
171 mutex_init(&dev_lck, NULL, MUTEX_DRIVER, NULL);
172
173 /* Connection data structures. */
174 (void) smb_sm_init();
175
176 /* Initialize password Key chain DB. */
177 smb_pkey_init();
178
179 /* Time conversion stuff. */
180 smb_time_init();
181
182 /* Initialize crypto mechanisms. */
183 smb_crypto_mech_init();
184
185 zone_key_create(&nsmb_zone_key, NULL, nsmb_zone_shutdown,
186 nsmb_zone_destroy);
187
188 /*
189 * Install the module. Do this after other init,
190 * to prevent entrances before we're ready.
191 */
192 if ((error = mod_install((&nsmb_modlinkage))) != 0) {
193
194 /* Same as 2nd half of _fini */
195 (void) zone_key_delete(nsmb_zone_key);
196 smb_pkey_fini();
197 smb_sm_done();
198 mutex_destroy(&dev_lck);
199 ddi_soft_state_fini(&statep);
200
201 return (error);
202 }
203
204 return (0);
205 }
206
207 int
_fini(void)208 _fini(void)
209 {
210 int status;
211
212 /*
213 * Prevent unload if we have active VCs
214 * or stored passwords
215 */
216 if ((status = smb_sm_idle()) != 0)
217 return (status);
218 if ((status = smb_pkey_idle()) != 0)
219 return (status);
220
221 /*
222 * Remove the module. Do this before destroying things,
223 * to prevent new entrances while we're destorying.
224 */
225 if ((status = mod_remove(&nsmb_modlinkage)) != 0) {
226 return (status);
227 }
228
229 (void) zone_key_delete(nsmb_zone_key);
230
231 /* Time conversion stuff. */
232 smb_time_fini();
233
234 /* Destroy password Key chain DB. */
235 smb_pkey_fini();
236
237 smb_sm_done();
238
239 mutex_destroy(&dev_lck);
240 ddi_soft_state_fini(&statep);
241
242 return (status);
243 }
244
245 int
_info(struct modinfo * modinfop)246 _info(struct modinfo *modinfop)
247 {
248 return (mod_info(&nsmb_modlinkage, modinfop));
249 }
250
251 /*ARGSUSED*/
252 static int
nsmb_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)253 nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
254 {
255 int ret = DDI_SUCCESS;
256
257 switch (cmd) {
258 case DDI_INFO_DEVT2DEVINFO:
259 *result = nsmb_dip;
260 break;
261 case DDI_INFO_DEVT2INSTANCE:
262 *result = NULL;
263 break;
264 default:
265 ret = DDI_FAILURE;
266 }
267 return (ret);
268 }
269
270 static int
nsmb_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)271 nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
272 {
273
274 if (cmd != DDI_ATTACH)
275 return (DDI_FAILURE);
276
277 /*
278 * We only support only one "instance". Note that
279 * "instances" are different from minor units.
280 * We get one (unique) minor unit per open.
281 */
282 if (ddi_get_instance(dip) > 0)
283 return (DDI_FAILURE);
284
285 if (ddi_create_minor_node(dip, "nsmb", S_IFCHR, 0, DDI_PSEUDO,
286 NULL) == DDI_FAILURE) {
287 cmn_err(CE_WARN, "nsmb_attach: create minor");
288 return (DDI_FAILURE);
289 }
290
291 /*
292 * We need the major number a couple places,
293 * i.e. in smb_dev2share()
294 */
295 nsmb_major = ddi_name_to_major(NSMB_NAME);
296
297 nsmb_dip = dip;
298 ddi_report_dev(dip);
299 return (DDI_SUCCESS);
300 }
301
302 /*ARGSUSED*/
303 static int
nsmb_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)304 nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
305 {
306
307 if (cmd != DDI_DETACH)
308 return (DDI_FAILURE);
309 if (ddi_get_instance(dip) > 0)
310 return (DDI_FAILURE);
311
312 nsmb_dip = NULL;
313 ddi_remove_minor_node(dip, NULL);
314
315 return (DDI_SUCCESS);
316 }
317
318 /*ARGSUSED*/
319 static int
nsmb_ioctl(dev_t dev,int cmd,intptr_t arg,int flags,cred_t * cr,int * rvalp)320 nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int flags, /* model.h */
321 cred_t *cr, int *rvalp)
322 {
323 smb_dev_t *sdp;
324 int err;
325
326 sdp = ddi_get_soft_state(statep, getminor(dev));
327 if (sdp == NULL) {
328 return (DDI_FAILURE);
329 }
330 if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
331 return (EBADF);
332 }
333
334 /*
335 * Dont give access if the zone id is not as the same as we
336 * set in the nsmb_open or dont belong to the global zone.
337 * Check if the user belongs to this zone..
338 */
339 if (sdp->zoneid != getzoneid())
340 return (EIO);
341
342 /*
343 * We have a zone_shutdown call back that kills all the VCs
344 * in a zone that's shutting down. That action will cause
345 * all of these ioctls to fail on such VCs, so no need to
346 * check the zone status here on every ioctl call.
347 */
348
349 err = 0;
350 switch (cmd) {
351 case SMBIOC_GETVERS:
352 (void) ddi_copyout(&nsmb_version, (void *)arg,
353 sizeof (nsmb_version), flags);
354 break;
355
356 case SMBIOC_FLAGS2:
357 err = smb_usr_get_flags2(sdp, arg, flags);
358 break;
359
360 case SMBIOC_GETSSNKEY:
361 err = smb_usr_get_ssnkey(sdp, arg, flags);
362 break;
363
364 case SMBIOC_DUP_DEV:
365 err = smb_usr_dup_dev(sdp, arg, flags);
366 break;
367
368 case SMBIOC_REQUEST:
369 err = smb_usr_simplerq(sdp, arg, flags, cr);
370 break;
371
372 case SMBIOC_T2RQ:
373 err = smb_usr_t2request(sdp, arg, flags, cr);
374 break;
375
376 case SMBIOC_READ:
377 case SMBIOC_WRITE:
378 err = smb_usr_rw(sdp, cmd, arg, flags, cr);
379 break;
380
381 case SMBIOC_NTCREATE:
382 err = smb_usr_ntcreate(sdp, arg, flags, cr);
383 break;
384
385 case SMBIOC_PRINTJOB:
386 err = smb_usr_printjob(sdp, arg, flags, cr);
387 break;
388
389 case SMBIOC_CLOSEFH:
390 err = smb_usr_closefh(sdp, cr);
391 break;
392
393 case SMBIOC_SSN_CREATE:
394 case SMBIOC_SSN_FIND:
395 err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr);
396 break;
397
398 case SMBIOC_SSN_KILL:
399 case SMBIOC_SSN_RELE:
400 err = smb_usr_drop_ssn(sdp, cmd);
401 break;
402
403 case SMBIOC_TREE_CONNECT:
404 case SMBIOC_TREE_FIND:
405 err = smb_usr_get_tree(sdp, cmd, arg, flags, cr);
406 break;
407
408 case SMBIOC_TREE_KILL:
409 case SMBIOC_TREE_RELE:
410 err = smb_usr_drop_tree(sdp, cmd);
411 break;
412
413 case SMBIOC_IOD_WORK:
414 err = smb_usr_iod_work(sdp, arg, flags, cr);
415 break;
416
417 case SMBIOC_IOD_IDLE:
418 case SMBIOC_IOD_RCFAIL:
419 err = smb_usr_iod_ioctl(sdp, cmd, arg, flags);
420 break;
421
422 case SMBIOC_PK_ADD:
423 case SMBIOC_PK_DEL:
424 case SMBIOC_PK_CHK:
425 case SMBIOC_PK_DEL_OWNER:
426 case SMBIOC_PK_DEL_EVERYONE:
427 err = smb_pkey_ioctl(cmd, arg, flags, cr);
428 break;
429
430 default:
431 err = ENOTTY;
432 break;
433 }
434
435 return (err);
436 }
437
438 /*
439 * This does "clone" open, meaning it automatically
440 * assigns an available minor unit for each open.
441 */
442 /*ARGSUSED*/
443 static int
nsmb_open(dev_t * dev,int flags,int otyp,cred_t * cr)444 nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr)
445 {
446 smb_dev_t *sdp;
447 minor_t m;
448
449 mutex_enter(&dev_lck);
450
451 for (m = last_minor + 1; m != last_minor; m++) {
452 if (m > NSMB_MAX_MINOR)
453 m = NSMB_MIN_MINOR;
454
455 if (ddi_get_soft_state(statep, m) == NULL) {
456 last_minor = m;
457 goto found;
458 }
459 }
460
461 /* No available minor units. */
462 mutex_exit(&dev_lck);
463 return (ENXIO);
464
465 found:
466 /* NB: dev_lck still held */
467 if (ddi_soft_state_zalloc(statep, m) == DDI_FAILURE) {
468 mutex_exit(&dev_lck);
469 return (ENXIO);
470 }
471 if ((sdp = ddi_get_soft_state(statep, m)) == NULL) {
472 mutex_exit(&dev_lck);
473 return (ENXIO);
474 }
475 *dev = makedevice(nsmb_major, m);
476 mutex_exit(&dev_lck);
477
478 sdp->sd_cred = cr;
479 sdp->sd_smbfid = -1;
480 sdp->sd_flags |= NSMBFL_OPEN;
481 sdp->zoneid = crgetzoneid(cr);
482
483 return (0);
484 }
485
486 /*ARGSUSED*/
487 static int
nsmb_close(dev_t dev,int flags,int otyp,cred_t * cr)488 nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr)
489 {
490 minor_t inst = getminor(dev);
491 smb_dev_t *sdp;
492 int err;
493
494 /*
495 * 1. Check the validity of the minor number.
496 * 2. Release any shares/vc associated with the connection.
497 * 3. Can close the minor number.
498 * 4. Deallocate any resources allocated in open() call.
499 */
500
501 sdp = ddi_get_soft_state(statep, inst);
502 if (sdp != NULL)
503 err = nsmb_close2(sdp, cr);
504 else
505 err = ENXIO;
506
507 /*
508 * Free the instance
509 */
510 mutex_enter(&dev_lck);
511 ddi_soft_state_free(statep, inst);
512 mutex_exit(&dev_lck);
513 return (err);
514 }
515
516 static int
nsmb_close2(smb_dev_t * sdp,cred_t * cr)517 nsmb_close2(smb_dev_t *sdp, cred_t *cr)
518 {
519 struct smb_vc *vcp;
520 struct smb_share *ssp;
521
522 if (sdp->sd_smbfid != -1)
523 (void) smb_usr_closefh(sdp, cr);
524
525 ssp = sdp->sd_share;
526 if (ssp != NULL)
527 smb_share_rele(ssp);
528
529 vcp = sdp->sd_vc;
530 if (vcp != NULL) {
531 /*
532 * If this dev minor was opened by smbiod,
533 * mark this VC as "dead" because it now
534 * will have no IOD to service it.
535 */
536 if (sdp->sd_flags & NSMBFL_IOD)
537 smb_iod_disconnect(vcp);
538 smb_vc_rele(vcp);
539 }
540
541 return (0);
542 }
543
544 /*
545 * Helper for SMBIOC_DUP_DEV
546 * Duplicate state from the FD @arg ("from") onto
547 * the FD for this device instance.
548 */
549 int
smb_usr_dup_dev(smb_dev_t * sdp,intptr_t arg,int flags)550 smb_usr_dup_dev(smb_dev_t *sdp, intptr_t arg, int flags)
551 {
552 file_t *fp = NULL;
553 vnode_t *vp;
554 smb_dev_t *from_sdp;
555 dev_t dev;
556 int32_t ufd;
557 int err;
558
559 /* Should be no VC */
560 if (sdp->sd_vc != NULL)
561 return (EISCONN);
562
563 /*
564 * Get from_sdp (what we will duplicate)
565 */
566 if (ddi_copyin((void *) arg, &ufd, sizeof (ufd), flags))
567 return (EFAULT);
568 if ((fp = getf(ufd)) == NULL)
569 return (EBADF);
570 /* rele fp below */
571 vp = fp->f_vnode;
572 dev = vp->v_rdev;
573 if (dev == 0 || dev == NODEV ||
574 getmajor(dev) != nsmb_major) {
575 err = EINVAL;
576 goto out;
577 }
578 from_sdp = ddi_get_soft_state(statep, getminor(dev));
579 if (from_sdp == NULL) {
580 err = EINVAL;
581 goto out;
582 }
583
584 /*
585 * Duplicate VC and share references onto this FD.
586 */
587 if ((sdp->sd_vc = from_sdp->sd_vc) != NULL)
588 smb_vc_hold(sdp->sd_vc);
589 if ((sdp->sd_share = from_sdp->sd_share) != NULL)
590 smb_share_hold(sdp->sd_share);
591 sdp->sd_level = from_sdp->sd_level;
592 err = 0;
593
594 out:
595 if (fp)
596 releasef(ufd);
597 return (err);
598 }
599
600
601 /*
602 * Helper used by smbfs_mount
603 */
604 int
smb_dev2share(int fd,struct smb_share ** sspp)605 smb_dev2share(int fd, struct smb_share **sspp)
606 {
607 file_t *fp = NULL;
608 vnode_t *vp;
609 smb_dev_t *sdp;
610 smb_share_t *ssp;
611 dev_t dev;
612 int err;
613
614 if ((fp = getf(fd)) == NULL)
615 return (EBADF);
616 /* rele fp below */
617
618 vp = fp->f_vnode;
619 dev = vp->v_rdev;
620 if (dev == 0 || dev == NODEV ||
621 getmajor(dev) != nsmb_major) {
622 err = EINVAL;
623 goto out;
624 }
625
626 sdp = ddi_get_soft_state(statep, getminor(dev));
627 if (sdp == NULL) {
628 err = EINVAL;
629 goto out;
630 }
631
632 ssp = sdp->sd_share;
633 if (ssp == NULL) {
634 err = ENOTCONN;
635 goto out;
636 }
637
638 /*
639 * Our caller gains a ref. to this share.
640 */
641 *sspp = ssp;
642 smb_share_hold(ssp);
643 err = 0;
644
645 out:
646 if (fp)
647 releasef(fd);
648 return (err);
649 }
650