xref: /freebsd/sys/netsmb/smb_dev.c (revision 1db64f89363c97858961c4df0b7d02f3223723cf)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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/param.h>
30 #include <sys/kernel.h>
31 #include <sys/capsicum.h>
32 #include <sys/module.h>
33 #include <sys/systm.h>
34 #include <sys/conf.h>
35 #include <sys/fcntl.h>
36 #include <sys/ioccom.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/file.h>		/* Must come after sys/malloc.h */
40 #include <sys/filedesc.h>
41 #include <sys/mbuf.h>
42 #include <sys/poll.h>
43 #include <sys/proc.h>
44 #include <sys/select.h>
45 #include <sys/socket.h>
46 #include <sys/socketvar.h>
47 #include <sys/sysctl.h>
48 #include <sys/uio.h>
49 #include <sys/vnode.h>
50 
51 #include <net/if.h>
52 
53 #include <netsmb/smb.h>
54 #include <netsmb/smb_conn.h>
55 #include <netsmb/smb_subr.h>
56 #include <netsmb/smb_dev.h>
57 
58 static struct cdev *nsmb_dev;
59 
60 static d_open_t	 nsmb_dev_open;
61 static d_ioctl_t nsmb_dev_ioctl;
62 
63 MODULE_DEPEND(netsmb, libiconv, 1, 1, 2);
64 MODULE_VERSION(netsmb, NSMB_VERSION);
65 
66 static int smb_version = NSMB_VERSION;
67 struct sx smb_lock;
68 
69 SYSCTL_DECL(_net_smb);
70 SYSCTL_INT(_net_smb, OID_AUTO, version, CTLFLAG_RD, &smb_version, 0, "");
71 
72 static MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device");
73 
74 static struct cdevsw nsmb_cdevsw = {
75 	.d_version =	D_VERSION,
76 	.d_open =	nsmb_dev_open,
77 	.d_ioctl =	nsmb_dev_ioctl,
78 	.d_name =	NSMB_NAME
79 };
80 
81 static int
82 nsmb_dev_init(void)
83 {
84 
85 	nsmb_dev = make_dev(&nsmb_cdevsw, 0, UID_ROOT, GID_OPERATOR,
86 	    0600, "nsmb");
87 	if (nsmb_dev == NULL)
88 		return (ENOMEM);
89 	return (0);
90 }
91 
92 static void
93 nsmb_dev_destroy(void)
94 {
95 
96 	MPASS(nsmb_dev != NULL);
97 	destroy_dev(nsmb_dev);
98 	nsmb_dev = NULL;
99 }
100 
101 static struct smb_dev *
102 smbdev_alloc(struct cdev *dev)
103 {
104 	struct smb_dev *sdp;
105 
106 	sdp = malloc(sizeof(struct smb_dev), M_NSMBDEV, M_WAITOK | M_ZERO);
107 	sdp->dev = dev;
108 	sdp->sd_level = -1;
109 	sdp->sd_flags |= NSMBFL_OPEN;
110 	sdp->refcount = 1;
111 	return (sdp);
112 }
113 
114 void
115 sdp_dtor(void *arg)
116 {
117 	struct smb_dev *dev;
118 
119 	dev = (struct smb_dev *)arg;
120 	SMB_LOCK();
121 	sdp_trydestroy(dev);
122 	SMB_UNLOCK();
123 }
124 
125 static int
126 nsmb_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
127 {
128 	struct smb_dev *sdp;
129 	int error;
130 
131 	sdp = smbdev_alloc(dev);
132 	error = devfs_set_cdevpriv(sdp, sdp_dtor);
133 	if (error) {
134 		free(sdp, M_NSMBDEV);
135 		return (error);
136 	}
137 	return (0);
138 }
139 
140 void
141 sdp_trydestroy(struct smb_dev *sdp)
142 {
143 	struct smb_vc *vcp;
144 	struct smb_share *ssp;
145 	struct smb_cred *scred;
146 
147 	SMB_LOCKASSERT();
148 	if (!sdp)
149 		panic("No smb_dev upon device close");
150 	MPASS(sdp->refcount > 0);
151 	sdp->refcount--;
152 	if (sdp->refcount)
153 		return;
154 	scred = malloc(sizeof(struct smb_cred), M_NSMBDEV, M_WAITOK);
155 	smb_makescred(scred, curthread, NULL);
156 	ssp = sdp->sd_share;
157 	if (ssp != NULL) {
158 		smb_share_lock(ssp);
159 		smb_share_rele(ssp, scred);
160 	}
161 	vcp = sdp->sd_vc;
162 	if (vcp != NULL) {
163 		smb_vc_lock(vcp);
164 		smb_vc_rele(vcp, scred);
165 	}
166 	free(scred, M_NSMBDEV);
167 	free(sdp, M_NSMBDEV);
168 	return;
169 }
170 
171 static int
172 nsmb_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
173 {
174 	struct smb_dev *sdp;
175 	struct smb_vc *vcp;
176 	struct smb_share *ssp;
177 	struct smb_cred *scred;
178 	int error = 0;
179 
180 	error = devfs_get_cdevpriv((void **)&sdp);
181 	if (error)
182 		return (error);
183 	scred = malloc(sizeof(struct smb_cred), M_NSMBDEV, M_WAITOK);
184 	SMB_LOCK();
185 	smb_makescred(scred, td, NULL);
186 	switch (cmd) {
187 	    case SMBIOC_OPENSESSION:
188 		if (sdp->sd_vc) {
189 			error = EISCONN;
190 			goto out;
191 		}
192 		error = smb_usr_opensession((struct smbioc_ossn*)data,
193 		    scred, &vcp);
194 		if (error)
195 			break;
196 		sdp->sd_vc = vcp;
197 		smb_vc_unlock(vcp);
198 		sdp->sd_level = SMBL_VC;
199 		break;
200 	    case SMBIOC_OPENSHARE:
201 		if (sdp->sd_share) {
202 			error = EISCONN;
203 			goto out;
204 		}
205 		if (sdp->sd_vc == NULL) {
206 			error = ENOTCONN;
207 			goto out;
208 		}
209 		error = smb_usr_openshare(sdp->sd_vc,
210 		    (struct smbioc_oshare*)data, scred, &ssp);
211 		if (error)
212 			break;
213 		sdp->sd_share = ssp;
214 		smb_share_unlock(ssp);
215 		sdp->sd_level = SMBL_SHARE;
216 		break;
217 	    case SMBIOC_REQUEST:
218 		if (sdp->sd_share == NULL) {
219 			error = ENOTCONN;
220 			goto out;
221 		}
222 		error = smb_usr_simplerequest(sdp->sd_share,
223 		    (struct smbioc_rq*)data, scred);
224 		break;
225 	    case SMBIOC_T2RQ:
226 		if (sdp->sd_share == NULL) {
227 			error = ENOTCONN;
228 			goto out;
229 		}
230 		error = smb_usr_t2request(sdp->sd_share,
231 		    (struct smbioc_t2rq*)data, scred);
232 		break;
233 	    case SMBIOC_SETFLAGS: {
234 		struct smbioc_flags *fl = (struct smbioc_flags*)data;
235 		int on;
236 
237 		if (fl->ioc_level == SMBL_VC) {
238 			if (fl->ioc_mask & SMBV_PERMANENT) {
239 				on = fl->ioc_flags & SMBV_PERMANENT;
240 				if ((vcp = sdp->sd_vc) == NULL) {
241 					error = ENOTCONN;
242 					goto out;
243 				}
244 				error = smb_vc_get(vcp, scred);
245 				if (error)
246 					break;
247 				if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) {
248 					vcp->obj.co_flags |= SMBV_PERMANENT;
249 					smb_vc_ref(vcp);
250 				} else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) {
251 					vcp->obj.co_flags &= ~SMBV_PERMANENT;
252 					smb_vc_rele(vcp, scred);
253 				}
254 				smb_vc_put(vcp, scred);
255 			} else
256 				error = EINVAL;
257 		} else if (fl->ioc_level == SMBL_SHARE) {
258 			if (fl->ioc_mask & SMBS_PERMANENT) {
259 				on = fl->ioc_flags & SMBS_PERMANENT;
260 				if ((ssp = sdp->sd_share) == NULL) {
261 					error = ENOTCONN;
262 					goto out;
263 				}
264 				error = smb_share_get(ssp, scred);
265 				if (error)
266 					break;
267 				if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) {
268 					ssp->obj.co_flags |= SMBS_PERMANENT;
269 					smb_share_ref(ssp);
270 				} else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) {
271 					ssp->obj.co_flags &= ~SMBS_PERMANENT;
272 					smb_share_rele(ssp, scred);
273 				}
274 				smb_share_put(ssp, scred);
275 			} else
276 				error = EINVAL;
277 			break;
278 		} else
279 			error = EINVAL;
280 		break;
281 	    }
282 	    case SMBIOC_LOOKUP:
283 		if (sdp->sd_vc || sdp->sd_share) {
284 			error = EISCONN;
285 			goto out;
286 		}
287 		vcp = NULL;
288 		ssp = NULL;
289 		error = smb_usr_lookup((struct smbioc_lookup*)data, scred, &vcp, &ssp);
290 		if (error)
291 			break;
292 		if (vcp) {
293 			sdp->sd_vc = vcp;
294 			smb_vc_unlock(vcp);
295 			sdp->sd_level = SMBL_VC;
296 		}
297 		if (ssp) {
298 			sdp->sd_share = ssp;
299 			smb_share_unlock(ssp);
300 			sdp->sd_level = SMBL_SHARE;
301 		}
302 		break;
303 	    case SMBIOC_READ: case SMBIOC_WRITE: {
304 		struct smbioc_rw *rwrq = (struct smbioc_rw*)data;
305 		struct uio auio;
306 		struct iovec iov;
307 
308 		if ((ssp = sdp->sd_share) == NULL) {
309 			error = ENOTCONN;
310 			goto out;
311 	 	}
312 		iov.iov_base = rwrq->ioc_base;
313 		iov.iov_len = rwrq->ioc_cnt;
314 		auio.uio_iov = &iov;
315 		auio.uio_iovcnt = 1;
316 		auio.uio_offset = rwrq->ioc_offset;
317 		auio.uio_resid = rwrq->ioc_cnt;
318 		auio.uio_segflg = UIO_USERSPACE;
319 		auio.uio_rw = (cmd == SMBIOC_READ) ? UIO_READ : UIO_WRITE;
320 		auio.uio_td = td;
321 		if (cmd == SMBIOC_READ)
322 			error = smb_read(ssp, rwrq->ioc_fh, &auio, scred);
323 		else
324 			error = smb_write(ssp, rwrq->ioc_fh, &auio, scred);
325 		rwrq->ioc_cnt -= auio.uio_resid;
326 		break;
327 	    }
328 	    default:
329 		error = ENODEV;
330 	}
331 out:
332 	free(scred, M_NSMBDEV);
333 	SMB_UNLOCK();
334 	return error;
335 }
336 
337 static int
338 nsmb_dev_load(module_t mod, int cmd, void *arg)
339 {
340 	int error = 0;
341 
342 	switch (cmd) {
343 	    case MOD_LOAD:
344 		error = smb_sm_init();
345 		if (error)
346 			break;
347 		error = smb_iod_init();
348 		if (error) {
349 			smb_sm_done();
350 			break;
351 		}
352 		error = nsmb_dev_init();
353 		if (error)
354 			break;
355 		sx_init(&smb_lock, "samba device lock");
356 		break;
357 	    case MOD_UNLOAD:
358 		smb_iod_done();
359 		error = smb_sm_done();
360 		if (error)
361 			break;
362 		nsmb_dev_destroy();
363 		sx_destroy(&smb_lock);
364 		break;
365 	    default:
366 		error = EINVAL;
367 		break;
368 	}
369 	return error;
370 }
371 
372 DEV_MODULE (dev_netsmb, nsmb_dev_load, 0);
373 
374 int
375 smb_dev2share(int fd, int mode, struct smb_cred *scred,
376 	struct smb_share **sspp, struct smb_dev **ssdp)
377 {
378 	struct file *fp, *fptmp;
379 	struct smb_dev *sdp;
380 	struct smb_share *ssp;
381 	struct thread *td;
382 	int error;
383 
384 	td = curthread;
385 	error = fget(td, fd, &cap_read_rights, &fp);
386 	if (error)
387 		return (error);
388 	fptmp = td->td_fpop;
389 	td->td_fpop = fp;
390 	error = devfs_get_cdevpriv((void **)&sdp);
391 	td->td_fpop = fptmp;
392 	fdrop(fp, td);
393 	if (error || sdp == NULL)
394 		return (error);
395 	SMB_LOCK();
396 	*ssdp = sdp;
397 	ssp = sdp->sd_share;
398 	if (ssp == NULL) {
399 		SMB_UNLOCK();
400 		return (ENOTCONN);
401 	}
402 	error = smb_share_get(ssp, scred);
403 	if (error == 0) {
404 		sdp->refcount++;
405 		*sspp = ssp;
406 	}
407 	SMB_UNLOCK();
408 	return error;
409 }
410