xref: /freebsd/sys/netsmb/smb_dev.c (revision b0d29bc47dba79f6f38e67eabadfb4b32ffd9390)
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