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 */
79 const 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
88 static void *statep;
89 static major_t nsmb_major;
90 static 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
98 static kmutex_t dev_lck;
99
100 /* Zone support */
101 zone_key_t nsmb_zone_key;
102 extern void nsmb_zone_shutdown(zoneid_t zoneid, void *data);
103 extern void nsmb_zone_destroy(zoneid_t zoneid, void *data);
104
105 /*
106 * cb_ops device operations.
107 */
108 static int nsmb_open(dev_t *devp, int flag, int otyp, cred_t *credp);
109 static int nsmb_close(dev_t dev, int flag, int otyp, cred_t *credp);
110 static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
111 cred_t *credp, int *rvalp);
112 static int nsmb_close2(smb_dev_t *sdp, cred_t *cr);
113
114 /* smbfs cb_ops */
115 static 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 */
139 static int nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
140 static int nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
141 static int nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
142 void *arg, void **result);
143
144 static 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
163 static struct modldrv nsmb_modldrv = {
164 &mod_driverops, /* Driver module */
165 "SMBFS network driver",
166 &nsmb_ops /* Driver ops */
167 };
168
169 static struct modlinkage nsmb_modlinkage = {
170 MODREV_1,
171 (void *)&nsmb_modldrv,
172 NULL
173 };
174
175 int
_init(void)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
224 int
_fini(void)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
262 int
_info(struct modinfo * modinfop)263 _info(struct modinfo *modinfop)
264 {
265 return (mod_info(&nsmb_modlinkage, modinfop));
266 }
267
268 /*ARGSUSED*/
269 static int
nsmb_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)270 nsmb_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
287 static int
nsmb_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)288 nsmb_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
332 attach_failed:
333 ddi_soft_state_free(statep, 0);
334 mutex_exit(&dev_lck);
335 return (DDI_FAILURE);
336 }
337
338 /*ARGSUSED*/
339 static int
nsmb_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)340 nsmb_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*/
355 static int
nsmb_ioctl(dev_t dev,int cmd,intptr_t arg,int flags,cred_t * cr,int * rvalp)356 nsmb_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*/
459 static int
nsmb_open(dev_t * dev,int flags,int otyp,cred_t * cr)460 nsmb_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*/
539 static int
nsmb_close(dev_t dev,int flags,int otyp,cred_t * cr)540 nsmb_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
568 static int
nsmb_close2(smb_dev_t * sdp,cred_t * cr)569 nsmb_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
595 int
smb_dev2share(int fd,struct smb_share ** sspp)596 smb_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
635 out:
636 if (fp)
637 releasef(fd);
638 return (err);
639 }
640