1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2000-2001 Boris Popov 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/capsicum.h> 35 #include <sys/module.h> 36 #include <sys/systm.h> 37 #include <sys/conf.h> 38 #include <sys/fcntl.h> 39 #include <sys/ioccom.h> 40 #include <sys/lock.h> 41 #include <sys/malloc.h> 42 #include <sys/file.h> /* Must come after sys/malloc.h */ 43 #include <sys/filedesc.h> 44 #include <sys/mbuf.h> 45 #include <sys/poll.h> 46 #include <sys/proc.h> 47 #include <sys/select.h> 48 #include <sys/socket.h> 49 #include <sys/socketvar.h> 50 #include <sys/sysctl.h> 51 #include <sys/uio.h> 52 #include <sys/vnode.h> 53 54 #include <net/if.h> 55 56 #include <netsmb/smb.h> 57 #include <netsmb/smb_conn.h> 58 #include <netsmb/smb_subr.h> 59 #include <netsmb/smb_dev.h> 60 61 static struct cdev *nsmb_dev; 62 63 static d_open_t nsmb_dev_open; 64 static d_ioctl_t nsmb_dev_ioctl; 65 66 MODULE_DEPEND(netsmb, libiconv, 1, 1, 2); 67 MODULE_VERSION(netsmb, NSMB_VERSION); 68 69 static int smb_version = NSMB_VERSION; 70 struct sx smb_lock; 71 72 73 SYSCTL_DECL(_net_smb); 74 SYSCTL_INT(_net_smb, OID_AUTO, version, CTLFLAG_RD, &smb_version, 0, ""); 75 76 static MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device"); 77 78 static struct cdevsw nsmb_cdevsw = { 79 .d_version = D_VERSION, 80 .d_open = nsmb_dev_open, 81 .d_ioctl = nsmb_dev_ioctl, 82 .d_name = NSMB_NAME 83 }; 84 85 static int 86 nsmb_dev_init(void) 87 { 88 89 nsmb_dev = make_dev(&nsmb_cdevsw, 0, UID_ROOT, GID_OPERATOR, 90 0600, "nsmb"); 91 if (nsmb_dev == NULL) 92 return (ENOMEM); 93 return (0); 94 } 95 96 static void 97 nsmb_dev_destroy(void) 98 { 99 100 MPASS(nsmb_dev != NULL); 101 destroy_dev(nsmb_dev); 102 nsmb_dev = NULL; 103 } 104 105 static struct smb_dev * 106 smbdev_alloc(struct cdev *dev) 107 { 108 struct smb_dev *sdp; 109 110 sdp = malloc(sizeof(struct smb_dev), M_NSMBDEV, M_WAITOK | M_ZERO); 111 sdp->dev = dev; 112 sdp->sd_level = -1; 113 sdp->sd_flags |= NSMBFL_OPEN; 114 sdp->refcount = 1; 115 return (sdp); 116 } 117 118 void 119 sdp_dtor(void *arg) 120 { 121 struct smb_dev *dev; 122 123 dev = (struct smb_dev *)arg; 124 SMB_LOCK(); 125 sdp_trydestroy(dev); 126 SMB_UNLOCK(); 127 } 128 129 static int 130 nsmb_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 131 { 132 struct smb_dev *sdp; 133 int error; 134 135 sdp = smbdev_alloc(dev); 136 error = devfs_set_cdevpriv(sdp, sdp_dtor); 137 if (error) { 138 free(sdp, M_NSMBDEV); 139 return (error); 140 } 141 return (0); 142 } 143 144 void 145 sdp_trydestroy(struct smb_dev *sdp) 146 { 147 struct smb_vc *vcp; 148 struct smb_share *ssp; 149 struct smb_cred *scred; 150 151 SMB_LOCKASSERT(); 152 if (!sdp) 153 panic("No smb_dev upon device close"); 154 MPASS(sdp->refcount > 0); 155 sdp->refcount--; 156 if (sdp->refcount) 157 return; 158 scred = malloc(sizeof(struct smb_cred), M_NSMBDEV, M_WAITOK); 159 smb_makescred(scred, curthread, NULL); 160 ssp = sdp->sd_share; 161 if (ssp != NULL) { 162 smb_share_lock(ssp); 163 smb_share_rele(ssp, scred); 164 } 165 vcp = sdp->sd_vc; 166 if (vcp != NULL) { 167 smb_vc_lock(vcp); 168 smb_vc_rele(vcp, scred); 169 } 170 free(scred, M_NSMBDEV); 171 free(sdp, M_NSMBDEV); 172 return; 173 } 174 175 176 static int 177 nsmb_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 178 { 179 struct smb_dev *sdp; 180 struct smb_vc *vcp; 181 struct smb_share *ssp; 182 struct smb_cred *scred; 183 int error = 0; 184 185 error = devfs_get_cdevpriv((void **)&sdp); 186 if (error) 187 return (error); 188 scred = malloc(sizeof(struct smb_cred), M_NSMBDEV, M_WAITOK); 189 SMB_LOCK(); 190 smb_makescred(scred, td, NULL); 191 switch (cmd) { 192 case SMBIOC_OPENSESSION: 193 if (sdp->sd_vc) { 194 error = EISCONN; 195 goto out; 196 } 197 error = smb_usr_opensession((struct smbioc_ossn*)data, 198 scred, &vcp); 199 if (error) 200 break; 201 sdp->sd_vc = vcp; 202 smb_vc_unlock(vcp); 203 sdp->sd_level = SMBL_VC; 204 break; 205 case SMBIOC_OPENSHARE: 206 if (sdp->sd_share) { 207 error = EISCONN; 208 goto out; 209 } 210 if (sdp->sd_vc == NULL) { 211 error = ENOTCONN; 212 goto out; 213 } 214 error = smb_usr_openshare(sdp->sd_vc, 215 (struct smbioc_oshare*)data, scred, &ssp); 216 if (error) 217 break; 218 sdp->sd_share = ssp; 219 smb_share_unlock(ssp); 220 sdp->sd_level = SMBL_SHARE; 221 break; 222 case SMBIOC_REQUEST: 223 if (sdp->sd_share == NULL) { 224 error = ENOTCONN; 225 goto out; 226 } 227 error = smb_usr_simplerequest(sdp->sd_share, 228 (struct smbioc_rq*)data, scred); 229 break; 230 case SMBIOC_T2RQ: 231 if (sdp->sd_share == NULL) { 232 error = ENOTCONN; 233 goto out; 234 } 235 error = smb_usr_t2request(sdp->sd_share, 236 (struct smbioc_t2rq*)data, scred); 237 break; 238 case SMBIOC_SETFLAGS: { 239 struct smbioc_flags *fl = (struct smbioc_flags*)data; 240 int on; 241 242 if (fl->ioc_level == SMBL_VC) { 243 if (fl->ioc_mask & SMBV_PERMANENT) { 244 on = fl->ioc_flags & SMBV_PERMANENT; 245 if ((vcp = sdp->sd_vc) == NULL) { 246 error = ENOTCONN; 247 goto out; 248 } 249 error = smb_vc_get(vcp, scred); 250 if (error) 251 break; 252 if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) { 253 vcp->obj.co_flags |= SMBV_PERMANENT; 254 smb_vc_ref(vcp); 255 } else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) { 256 vcp->obj.co_flags &= ~SMBV_PERMANENT; 257 smb_vc_rele(vcp, scred); 258 } 259 smb_vc_put(vcp, scred); 260 } else 261 error = EINVAL; 262 } else if (fl->ioc_level == SMBL_SHARE) { 263 if (fl->ioc_mask & SMBS_PERMANENT) { 264 on = fl->ioc_flags & SMBS_PERMANENT; 265 if ((ssp = sdp->sd_share) == NULL) { 266 error = ENOTCONN; 267 goto out; 268 } 269 error = smb_share_get(ssp, scred); 270 if (error) 271 break; 272 if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) { 273 ssp->obj.co_flags |= SMBS_PERMANENT; 274 smb_share_ref(ssp); 275 } else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) { 276 ssp->obj.co_flags &= ~SMBS_PERMANENT; 277 smb_share_rele(ssp, scred); 278 } 279 smb_share_put(ssp, scred); 280 } else 281 error = EINVAL; 282 break; 283 } else 284 error = EINVAL; 285 break; 286 } 287 case SMBIOC_LOOKUP: 288 if (sdp->sd_vc || sdp->sd_share) { 289 error = EISCONN; 290 goto out; 291 } 292 vcp = NULL; 293 ssp = NULL; 294 error = smb_usr_lookup((struct smbioc_lookup*)data, scred, &vcp, &ssp); 295 if (error) 296 break; 297 if (vcp) { 298 sdp->sd_vc = vcp; 299 smb_vc_unlock(vcp); 300 sdp->sd_level = SMBL_VC; 301 } 302 if (ssp) { 303 sdp->sd_share = ssp; 304 smb_share_unlock(ssp); 305 sdp->sd_level = SMBL_SHARE; 306 } 307 break; 308 case SMBIOC_READ: case SMBIOC_WRITE: { 309 struct smbioc_rw *rwrq = (struct smbioc_rw*)data; 310 struct uio auio; 311 struct iovec iov; 312 313 if ((ssp = sdp->sd_share) == NULL) { 314 error = ENOTCONN; 315 goto out; 316 } 317 iov.iov_base = rwrq->ioc_base; 318 iov.iov_len = rwrq->ioc_cnt; 319 auio.uio_iov = &iov; 320 auio.uio_iovcnt = 1; 321 auio.uio_offset = rwrq->ioc_offset; 322 auio.uio_resid = rwrq->ioc_cnt; 323 auio.uio_segflg = UIO_USERSPACE; 324 auio.uio_rw = (cmd == SMBIOC_READ) ? UIO_READ : UIO_WRITE; 325 auio.uio_td = td; 326 if (cmd == SMBIOC_READ) 327 error = smb_read(ssp, rwrq->ioc_fh, &auio, scred); 328 else 329 error = smb_write(ssp, rwrq->ioc_fh, &auio, scred); 330 rwrq->ioc_cnt -= auio.uio_resid; 331 break; 332 } 333 default: 334 error = ENODEV; 335 } 336 out: 337 free(scred, M_NSMBDEV); 338 SMB_UNLOCK(); 339 return error; 340 } 341 342 static int 343 nsmb_dev_load(module_t mod, int cmd, void *arg) 344 { 345 int error = 0; 346 347 switch (cmd) { 348 case MOD_LOAD: 349 error = smb_sm_init(); 350 if (error) 351 break; 352 error = smb_iod_init(); 353 if (error) { 354 smb_sm_done(); 355 break; 356 } 357 error = nsmb_dev_init(); 358 if (error) 359 break; 360 sx_init(&smb_lock, "samba device lock"); 361 break; 362 case MOD_UNLOAD: 363 smb_iod_done(); 364 error = smb_sm_done(); 365 if (error) 366 break; 367 nsmb_dev_destroy(); 368 sx_destroy(&smb_lock); 369 break; 370 default: 371 error = EINVAL; 372 break; 373 } 374 return error; 375 } 376 377 DEV_MODULE (dev_netsmb, nsmb_dev_load, 0); 378 379 int 380 smb_dev2share(int fd, int mode, struct smb_cred *scred, 381 struct smb_share **sspp, struct smb_dev **ssdp) 382 { 383 struct file *fp, *fptmp; 384 struct smb_dev *sdp; 385 struct smb_share *ssp; 386 struct thread *td; 387 int error; 388 389 td = curthread; 390 error = fget(td, fd, &cap_read_rights, &fp); 391 if (error) 392 return (error); 393 fptmp = td->td_fpop; 394 td->td_fpop = fp; 395 error = devfs_get_cdevpriv((void **)&sdp); 396 td->td_fpop = fptmp; 397 fdrop(fp, td); 398 if (error || sdp == NULL) 399 return (error); 400 SMB_LOCK(); 401 *ssdp = sdp; 402 ssp = sdp->sd_share; 403 if (ssp == NULL) { 404 SMB_UNLOCK(); 405 return (ENOTCONN); 406 } 407 error = smb_share_get(ssp, scred); 408 if (error == 0) { 409 sdp->refcount++; 410 *sspp = ssp; 411 } 412 SMB_UNLOCK(); 413 return error; 414 } 415 416