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
nsmb_dev_init(void)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
nsmb_dev_destroy(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 *
smbdev_alloc(struct cdev * 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
sdp_dtor(void * arg)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
nsmb_dev_open(struct cdev * dev,int oflags,int devtype,struct thread * td)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
sdp_trydestroy(struct smb_dev * sdp)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
nsmb_dev_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int flag,struct thread * td)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
nsmb_dev_load(module_t mod,int cmd,void * arg)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
smb_dev2share(int fd,int mode,struct smb_cred * scred,struct smb_share ** sspp,struct smb_dev ** ssdp)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