1 | /* $NetBSD: smb_conn.c,v 1.29 2012/04/29 20:27:31 dsl Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2008 The NetBSD Foundation, Inc. |
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
17 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
20 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | * POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | /* |
30 | * Copyright (c) 2000-2001 Boris Popov |
31 | * All rights reserved. |
32 | * |
33 | * Redistribution and use in source and binary forms, with or without |
34 | * modification, are permitted provided that the following conditions |
35 | * are met: |
36 | * 1. Redistributions of source code must retain the above copyright |
37 | * notice, this list of conditions and the following disclaimer. |
38 | * 2. Redistributions in binary form must reproduce the above copyright |
39 | * notice, this list of conditions and the following disclaimer in the |
40 | * documentation and/or other materials provided with the distribution. |
41 | * 3. All advertising materials mentioning features or use of this software |
42 | * must display the following acknowledgement: |
43 | * This product includes software developed by Boris Popov. |
44 | * 4. Neither the name of the author nor the names of any co-contributors |
45 | * may be used to endorse or promote products derived from this software |
46 | * without specific prior written permission. |
47 | * |
48 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
49 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
50 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
51 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
52 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
53 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
54 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
55 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
56 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
57 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
58 | * SUCH DAMAGE. |
59 | * |
60 | * FreeBSD: src/sys/netsmb/smb_conn.c,v 1.3 2001/12/02 08:47:29 bp Exp |
61 | */ |
62 | |
63 | #include <sys/cdefs.h> |
64 | __KERNEL_RCSID(0, "$NetBSD: smb_conn.c,v 1.29 2012/04/29 20:27:31 dsl Exp $" ); |
65 | |
66 | /* |
67 | * Connection engine. |
68 | */ |
69 | |
70 | #include <sys/param.h> |
71 | #include <sys/systm.h> |
72 | #include <sys/kernel.h> |
73 | #include <sys/malloc.h> |
74 | #include <sys/proc.h> |
75 | #include <sys/lock.h> |
76 | #include <sys/sysctl.h> |
77 | #include <sys/mbuf.h> /* for M_SONAME */ |
78 | #include <sys/kauth.h> |
79 | |
80 | #include <netsmb/iconv.h> |
81 | |
82 | #include <netsmb/smb.h> |
83 | #include <netsmb/smb_subr.h> |
84 | #include <netsmb/smb_conn.h> |
85 | #include <netsmb/smb_tran.h> |
86 | #include <netsmb/smb_trantcp.h> |
87 | |
88 | static struct smb_connobj smb_vclist; |
89 | static int smb_vcnext = 1; /* next unique id for VC */ |
90 | static kauth_listener_t smb_listener; |
91 | |
92 | MALLOC_DEFINE(M_SMBCONN, "SMB conn" , "SMB connection" ); |
93 | MALLOC_DECLARE(M_SMBCONN); |
94 | |
95 | static void smb_co_init(struct smb_connobj *cp, int level, const char *objname); |
96 | static void smb_co_done(struct smb_connobj *cp); |
97 | |
98 | static int smb_vc_disconnect(struct smb_vc *vcp); |
99 | static void smb_vc_free(struct smb_connobj *cp); |
100 | static void smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred); |
101 | static smb_co_free_t smb_share_free; |
102 | static smb_co_gone_t smb_share_gone; |
103 | |
104 | static int |
105 | smb_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, |
106 | void *arg0, void *arg1, void *arg2, void *arg3) |
107 | { |
108 | int result, ismember = 0; |
109 | enum kauth_network_req req; |
110 | |
111 | if (action != KAUTH_NETWORK_SMB) |
112 | return KAUTH_RESULT_DEFER; |
113 | |
114 | result = KAUTH_RESULT_DEFER; |
115 | req = (enum kauth_network_req)arg0; |
116 | |
117 | switch (req) { |
118 | case KAUTH_REQ_NETWORK_SMB_SHARE_ACCESS: { |
119 | struct smb_share *ssp = arg1; |
120 | mode_t mode = (mode_t)(uintptr_t)arg2; |
121 | |
122 | /* Owner can access. */ |
123 | if (kauth_cred_geteuid(cred) == ssp->ss_uid) { |
124 | result = KAUTH_RESULT_ALLOW; |
125 | break; |
126 | } |
127 | |
128 | /* Try group permissions if member or other if not. */ |
129 | mode >>= 3; |
130 | if (kauth_cred_ismember_gid(cred, ssp->ss_grp, &ismember) != 0 || |
131 | !ismember) |
132 | mode >>= 3; |
133 | |
134 | if ((ssp->ss_mode & mode) == mode) |
135 | result = KAUTH_RESULT_ALLOW; |
136 | |
137 | break; |
138 | } |
139 | |
140 | case KAUTH_REQ_NETWORK_SMB_SHARE_CREATE: { |
141 | struct smb_sharespec *shspec = arg1; |
142 | |
143 | /* |
144 | * Only superuser can create shares with different uid and gid |
145 | */ |
146 | if (shspec->owner != SMBM_ANY_OWNER && |
147 | shspec->owner != kauth_cred_geteuid(cred)) |
148 | break; |
149 | if (shspec->group != SMBM_ANY_GROUP && |
150 | (kauth_cred_ismember_gid(cred, shspec->group, &ismember) != 0 || !ismember)) |
151 | break; |
152 | |
153 | result = KAUTH_RESULT_ALLOW; |
154 | |
155 | break; |
156 | } |
157 | |
158 | case KAUTH_REQ_NETWORK_SMB_VC_ACCESS: { |
159 | struct smb_vc *vcp = arg1; |
160 | mode_t mode = (mode_t)(uintptr_t)arg2; |
161 | |
162 | /* Owner can access. */ |
163 | if (kauth_cred_geteuid(cred) == vcp->vc_uid) { |
164 | result = KAUTH_RESULT_ALLOW; |
165 | break; |
166 | } |
167 | |
168 | /* Try group permissions if member or other if not. */ |
169 | mode >>= 3; |
170 | if (kauth_cred_ismember_gid(cred, vcp->vc_grp, &ismember) != 0 || |
171 | !ismember) |
172 | mode >>= 3; |
173 | |
174 | if ((vcp->vc_mode & mode) == mode) |
175 | result = KAUTH_RESULT_ALLOW; |
176 | |
177 | break; |
178 | } |
179 | |
180 | case KAUTH_REQ_NETWORK_SMB_VC_CREATE: { |
181 | struct smb_vcspec *vcspec = arg1; |
182 | |
183 | /* |
184 | * Only superuser can create VCs with different uid and gid |
185 | */ |
186 | if (vcspec->owner != SMBM_ANY_OWNER && |
187 | vcspec->owner != kauth_cred_geteuid(cred)) |
188 | break; |
189 | if (vcspec->group != SMBM_ANY_GROUP && |
190 | (kauth_cred_ismember_gid(cred, vcspec->group, &ismember) != 0 || !ismember)) |
191 | break; |
192 | |
193 | result = KAUTH_RESULT_ALLOW; |
194 | |
195 | break; |
196 | } |
197 | |
198 | default: |
199 | break; |
200 | } |
201 | |
202 | return result; |
203 | } |
204 | |
205 | int |
206 | smb_sm_init(void) |
207 | { |
208 | |
209 | smb_co_init(&smb_vclist, SMBL_SM, "smbsm" ); |
210 | mutex_enter(&smb_vclist.co_interlock); |
211 | smb_co_unlock(&smb_vclist); |
212 | mutex_exit(&smb_vclist.co_interlock); |
213 | smb_listener = kauth_listen_scope(KAUTH_SCOPE_NETWORK, |
214 | smb_listener_cb, NULL); |
215 | return 0; |
216 | } |
217 | |
218 | int |
219 | smb_sm_done(void) |
220 | { |
221 | |
222 | /* XXX: hold the mutex */ |
223 | #ifdef DIAGNOSTIC |
224 | if (smb_vclist.co_usecount > 1) |
225 | panic("%d connections still active" , smb_vclist.co_usecount - 1); |
226 | #endif |
227 | smb_co_done(&smb_vclist); |
228 | kauth_unlisten_scope(smb_listener); |
229 | return 0; |
230 | } |
231 | |
232 | static int |
233 | smb_sm_lockvclist(void) |
234 | { |
235 | int error; |
236 | |
237 | mutex_enter(&smb_vclist.co_interlock); |
238 | error = smb_co_lock(&smb_vclist); |
239 | mutex_exit(&smb_vclist.co_interlock); |
240 | |
241 | return error; |
242 | } |
243 | |
244 | static void |
245 | smb_sm_unlockvclist(void) |
246 | { |
247 | |
248 | mutex_enter(&smb_vclist.co_interlock); |
249 | smb_co_unlock(&smb_vclist); |
250 | mutex_exit(&smb_vclist.co_interlock); |
251 | } |
252 | |
253 | static int |
254 | smb_sm_lookupint(struct smb_vcspec *vcspec, struct smb_sharespec *shspec, |
255 | struct smb_cred *scred, struct smb_vc **vcpp) |
256 | { |
257 | struct smb_connobj *ocp; |
258 | int exact = 1; |
259 | int fail = 1; |
260 | |
261 | vcspec->shspec = shspec; |
262 | SMBCO_FOREACH(ocp, &smb_vclist) { |
263 | struct smb_vc *vcp = (struct smb_vc *)ocp; |
264 | |
265 | if (smb_vc_lock(vcp) != 0) |
266 | continue; |
267 | |
268 | do { |
269 | if ((ocp->co_flags & SMBV_PRIVATE) || |
270 | !CONNADDREQ(vcp->vc_paddr, vcspec->sap) || |
271 | strcmp(vcp->vc_username, vcspec->username) != 0) |
272 | break; |
273 | |
274 | if (vcspec->owner != SMBM_ANY_OWNER) { |
275 | if (vcp->vc_uid != vcspec->owner) |
276 | break; |
277 | } else |
278 | exact = 0; |
279 | if (vcspec->group != SMBM_ANY_GROUP) { |
280 | if (vcp->vc_grp != vcspec->group) |
281 | break; |
282 | } else |
283 | exact = 0; |
284 | |
285 | if (vcspec->mode & SMBM_EXACT) { |
286 | if (!exact || |
287 | (vcspec->mode & SMBM_MASK) != vcp->vc_mode) |
288 | break; |
289 | } |
290 | if (smb_vc_access(vcp, scred, vcspec->mode) != 0) |
291 | break; |
292 | vcspec->ssp = NULL; |
293 | if (shspec |
294 | &&smb_vc_lookupshare(vcp, shspec, scred, &vcspec->ssp) != 0) |
295 | break; |
296 | |
297 | /* if we get here, all checks succeeded */ |
298 | smb_vc_ref(vcp); |
299 | *vcpp = vcp; |
300 | fail = 0; |
301 | goto out; |
302 | } while(0); |
303 | |
304 | smb_vc_unlock(vcp); |
305 | } |
306 | |
307 | out: |
308 | return fail; |
309 | } |
310 | |
311 | int |
312 | smb_sm_lookup(struct smb_vcspec *vcspec, struct smb_sharespec *shspec, |
313 | struct smb_cred *scred, struct smb_vc **vcpp) |
314 | { |
315 | struct smb_vc *vcp; |
316 | struct smb_share *ssp = NULL; |
317 | int fail, error; |
318 | |
319 | *vcpp = vcp = NULL; |
320 | |
321 | error = smb_sm_lockvclist(); |
322 | if (error) |
323 | return error; |
324 | fail = smb_sm_lookupint(vcspec, shspec, scred, vcpp); |
325 | if (!fail || (vcspec->flags & SMBV_CREATE) == 0) { |
326 | smb_sm_unlockvclist(); |
327 | return 0; |
328 | } |
329 | fail = smb_sm_lookupint(vcspec, NULL, scred, &vcp); |
330 | if (fail) { |
331 | error = smb_vc_create(vcspec, scred, &vcp); |
332 | if (error) |
333 | goto out; |
334 | error = smb_vc_connect(vcp, scred); |
335 | if (error) |
336 | goto out; |
337 | } |
338 | if (shspec == NULL) |
339 | goto out; |
340 | error = smb_share_create(vcp, shspec, scred, &ssp); |
341 | if (error) |
342 | goto out; |
343 | error = smb_smb_treeconnect(ssp, scred); |
344 | if (error == 0) |
345 | vcspec->ssp = ssp; |
346 | else |
347 | smb_share_put(ssp, scred); |
348 | out: |
349 | smb_sm_unlockvclist(); |
350 | if (error == 0) |
351 | *vcpp = vcp; |
352 | else if (vcp) |
353 | smb_vc_put(vcp, scred); |
354 | return error; |
355 | } |
356 | |
357 | /* |
358 | * Common code for connection object |
359 | */ |
360 | static void |
361 | smb_co_init(struct smb_connobj *cp, int level, const char *objname) |
362 | { |
363 | SLIST_INIT(&cp->co_children); |
364 | smb_sl_init(&cp->co_interlock, objname); |
365 | cv_init(&cp->co_lock, "smblock" ); |
366 | cp->co_lockcnt = 0; |
367 | cp->co_locker = NULL; |
368 | cp->co_level = level; |
369 | cp->co_usecount = 1; |
370 | mutex_enter(&cp->co_interlock); |
371 | smb_co_lock(cp); |
372 | mutex_exit(&cp->co_interlock); |
373 | } |
374 | |
375 | static void |
376 | smb_co_done(struct smb_connobj *cp) |
377 | { |
378 | smb_sl_destroy(&cp->co_interlock); |
379 | cv_destroy(&cp->co_lock); |
380 | } |
381 | |
382 | static void |
383 | smb_co_gone(struct smb_connobj *cp, struct smb_cred *scred) |
384 | { |
385 | struct smb_connobj *parent; |
386 | |
387 | if (cp->co_gone) |
388 | cp->co_gone(cp, scred); |
389 | parent = cp->co_parent; |
390 | if (parent) { |
391 | mutex_enter(&parent->co_interlock); |
392 | smb_co_lock(parent); |
393 | mutex_exit(&parent->co_interlock); |
394 | SLIST_REMOVE(&parent->co_children, cp, smb_connobj, co_next); |
395 | smb_co_put(parent, scred); |
396 | } |
397 | if (cp->co_free) |
398 | cp->co_free(cp); |
399 | } |
400 | |
401 | void |
402 | smb_co_ref(struct smb_connobj *cp) |
403 | { |
404 | |
405 | mutex_enter(&cp->co_interlock); |
406 | cp->co_usecount++; |
407 | mutex_exit(&cp->co_interlock); |
408 | } |
409 | |
410 | void |
411 | smb_co_rele(struct smb_connobj *cp, struct smb_cred *scred) |
412 | { |
413 | mutex_enter(&cp->co_interlock); |
414 | smb_co_unlock(cp); |
415 | if (cp->co_usecount > 1) { |
416 | cp->co_usecount--; |
417 | mutex_exit(&cp->co_interlock); |
418 | return; |
419 | } |
420 | #ifdef DIAGNOSTIC |
421 | if (cp->co_usecount == 0) |
422 | panic("negative use_count for object %d" , cp->co_level); |
423 | #endif |
424 | cp->co_usecount--; |
425 | cp->co_flags |= SMBO_GONE; |
426 | mutex_exit(&cp->co_interlock); |
427 | |
428 | smb_co_gone(cp, scred); |
429 | } |
430 | |
431 | int |
432 | smb_co_get(struct smb_connobj *cp, struct smb_cred *scred) |
433 | { |
434 | int error; |
435 | |
436 | KASSERT(mutex_owned(&cp->co_interlock)); |
437 | cp->co_usecount++; |
438 | error = smb_co_lock(cp); |
439 | if (error) |
440 | cp->co_usecount--; |
441 | return error; |
442 | } |
443 | |
444 | void |
445 | smb_co_put(struct smb_connobj *cp, struct smb_cred *scred) |
446 | { |
447 | |
448 | mutex_enter(&cp->co_interlock); |
449 | if (cp->co_usecount > 1) { |
450 | cp->co_usecount--; |
451 | } else if (cp->co_usecount == 1) { |
452 | cp->co_usecount--; |
453 | cp->co_flags |= SMBO_GONE; |
454 | } |
455 | #ifdef DIAGNOSTIC |
456 | else |
457 | panic("smb_co_put: negative usecount" ); |
458 | #endif |
459 | smb_co_unlock(cp); |
460 | mutex_exit(&cp->co_interlock); |
461 | if ((cp->co_flags & SMBO_GONE) == 0) |
462 | return; |
463 | smb_co_gone(cp, scred); |
464 | } |
465 | |
466 | int |
467 | smb_co_lock(struct smb_connobj *cp) |
468 | { |
469 | |
470 | KASSERT(mutex_owned(&cp->co_interlock)); |
471 | |
472 | for (;;) { |
473 | if (cp->co_flags & SMBO_GONE) |
474 | return EINVAL; |
475 | if (cp->co_locker == NULL) { |
476 | cp->co_locker = curlwp; |
477 | return 0; |
478 | } |
479 | if (cp->co_locker == curlwp) { |
480 | cp->co_lockcnt++; |
481 | return 0; |
482 | } |
483 | cv_wait(&cp->co_lock, &cp->co_interlock); |
484 | } |
485 | } |
486 | |
487 | void |
488 | smb_co_unlock(struct smb_connobj *cp) |
489 | { |
490 | |
491 | KASSERT(mutex_owned(&cp->co_interlock)); |
492 | KASSERT(cp->co_locker == curlwp); |
493 | |
494 | if (cp->co_lockcnt != 0) { |
495 | cp->co_lockcnt--; |
496 | return; |
497 | } |
498 | cp->co_locker = NULL; |
499 | cv_signal(&cp->co_lock); |
500 | } |
501 | |
502 | static void |
503 | smb_co_addchild(struct smb_connobj *parent, struct smb_connobj *child) |
504 | { |
505 | |
506 | smb_co_ref(parent); |
507 | SLIST_INSERT_HEAD(&parent->co_children, child, co_next); |
508 | child->co_parent = parent; |
509 | } |
510 | |
511 | /* |
512 | * Session implementation |
513 | */ |
514 | |
515 | int |
516 | smb_vc_create(struct smb_vcspec *vcspec, |
517 | struct smb_cred *scred, struct smb_vc **vcpp) |
518 | { |
519 | struct smb_vc *vcp; |
520 | kauth_cred_t cred = scred->scr_cred; |
521 | uid_t uid = vcspec->owner; |
522 | gid_t gid = vcspec->group; |
523 | uid_t realuid; |
524 | char *domain = vcspec->domain; |
525 | int error; |
526 | |
527 | error = kauth_authorize_network(cred, KAUTH_NETWORK_SMB, |
528 | KAUTH_REQ_NETWORK_SMB_VC_CREATE, vcspec, NULL, NULL); |
529 | if (error) |
530 | return EPERM; |
531 | |
532 | realuid = kauth_cred_geteuid(cred); |
533 | |
534 | vcp = smb_zmalloc(sizeof(*vcp), M_SMBCONN, M_WAITOK); |
535 | smb_co_init(VCTOCP(vcp), SMBL_VC, "smb_vc" ); |
536 | vcp->obj.co_free = smb_vc_free; |
537 | vcp->obj.co_gone = smb_vc_gone; |
538 | vcp->vc_number = smb_vcnext++; |
539 | vcp->vc_smbuid = SMB_UID_UNKNOWN; |
540 | vcp->vc_mode = vcspec->rights & SMBM_MASK; |
541 | vcp->obj.co_flags = vcspec->flags & (SMBV_PRIVATE | SMBV_SINGLESHARE); |
542 | vcp->vc_tdesc = &smb_tran_nbtcp_desc; |
543 | |
544 | if (uid == SMBM_ANY_OWNER) |
545 | uid = realuid; |
546 | if (gid == SMBM_ANY_GROUP) |
547 | gid = kauth_cred_group(cred, 0); |
548 | vcp->vc_uid = uid; |
549 | vcp->vc_grp = gid; |
550 | |
551 | smb_sl_init(&vcp->vc_stlock, "vcstlock" ); |
552 | error = ENOMEM; |
553 | if ((vcp->vc_paddr = dup_sockaddr(vcspec->sap, 1)) == NULL) |
554 | goto fail; |
555 | |
556 | if ((vcp->vc_laddr = dup_sockaddr(vcspec->lap, 1)) == NULL) |
557 | goto fail; |
558 | |
559 | if ((vcp->vc_pass = smb_strdup(vcspec->pass)) == NULL) |
560 | goto fail; |
561 | |
562 | vcp->vc_domain = smb_strdup((domain && domain[0]) ? domain : "NODOMAIN" ); |
563 | if (vcp->vc_domain == NULL) |
564 | goto fail; |
565 | |
566 | if ((vcp->vc_srvname = smb_strdup(vcspec->srvname)) == NULL) |
567 | goto fail; |
568 | |
569 | if ((vcp->vc_username = smb_strdup(vcspec->username)) == NULL) |
570 | goto fail; |
571 | |
572 | #define ithrow(cmd) \ |
573 | if ((error = cmd)) \ |
574 | goto fail |
575 | |
576 | ithrow(iconv_open("tolower" , vcspec->localcs, &vcp->vc_tolower)); |
577 | ithrow(iconv_open("toupper" , vcspec->localcs, &vcp->vc_toupper)); |
578 | if (vcspec->servercs[0]) { |
579 | ithrow(iconv_open(vcspec->servercs, vcspec->localcs, |
580 | &vcp->vc_toserver)); |
581 | ithrow(iconv_open(vcspec->localcs, vcspec->servercs, |
582 | &vcp->vc_tolocal)); |
583 | } |
584 | |
585 | ithrow(smb_iod_create(vcp)); |
586 | |
587 | #undef ithrow |
588 | |
589 | /* all is well, return success */ |
590 | *vcpp = vcp; |
591 | smb_co_addchild(&smb_vclist, VCTOCP(vcp)); |
592 | |
593 | return 0; |
594 | |
595 | fail: |
596 | smb_vc_put(vcp, scred); |
597 | return (error); |
598 | |
599 | } |
600 | |
601 | static void |
602 | smb_vc_free(struct smb_connobj *cp) |
603 | { |
604 | struct smb_vc *vcp = CPTOVC(cp); |
605 | |
606 | if (vcp->vc_iod) |
607 | smb_iod_destroy(vcp->vc_iod); |
608 | SMB_STRFREE(vcp->vc_username); |
609 | SMB_STRFREE(vcp->vc_srvname); |
610 | SMB_STRFREE(vcp->vc_pass); |
611 | SMB_STRFREE(vcp->vc_domain); |
612 | if (vcp->vc_paddr) |
613 | free(vcp->vc_paddr, M_SONAME); |
614 | if (vcp->vc_laddr) |
615 | free(vcp->vc_laddr, M_SONAME); |
616 | if (vcp->vc_tolower) |
617 | iconv_close(vcp->vc_tolower); |
618 | if (vcp->vc_toupper) |
619 | iconv_close(vcp->vc_toupper); |
620 | if (vcp->vc_tolocal) |
621 | iconv_close(vcp->vc_tolocal); |
622 | if (vcp->vc_toserver) |
623 | iconv_close(vcp->vc_toserver); |
624 | smb_co_done(VCTOCP(vcp)); |
625 | smb_sl_destroy(&vcp->vc_stlock); |
626 | free(vcp, M_SMBCONN); |
627 | } |
628 | |
629 | /* |
630 | * Called when use count of VC dropped to zero. |
631 | * VC should be locked on enter with LK_DRAIN. |
632 | */ |
633 | static void |
634 | smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred) |
635 | { |
636 | struct smb_vc *vcp = CPTOVC(cp); |
637 | |
638 | smb_vc_disconnect(vcp); |
639 | } |
640 | |
641 | void |
642 | smb_vc_ref(struct smb_vc *vcp) |
643 | { |
644 | smb_co_ref(VCTOCP(vcp)); |
645 | } |
646 | |
647 | void |
648 | smb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred) |
649 | { |
650 | smb_co_rele(VCTOCP(vcp), scred); |
651 | } |
652 | |
653 | int |
654 | smb_vc_get(struct smb_vc *vcp, struct smb_cred *scred) |
655 | { |
656 | struct smb_connobj *cp = VCTOCP(vcp); |
657 | int error; |
658 | |
659 | mutex_enter(&cp->co_interlock); |
660 | error = smb_co_get(cp, scred); |
661 | mutex_exit(&cp->co_interlock); |
662 | |
663 | return error; |
664 | } |
665 | |
666 | void |
667 | smb_vc_put(struct smb_vc *vcp, struct smb_cred *scred) |
668 | { |
669 | smb_co_put(VCTOCP(vcp), scred); |
670 | } |
671 | |
672 | int |
673 | smb_vc_lock(struct smb_vc *vcp) |
674 | { |
675 | struct smb_connobj *cp = VCTOCP(vcp); |
676 | int error; |
677 | |
678 | mutex_enter(&cp->co_interlock); |
679 | error = smb_co_lock(cp); |
680 | mutex_exit(&cp->co_interlock); |
681 | |
682 | return error; |
683 | } |
684 | |
685 | void |
686 | smb_vc_unlock(struct smb_vc *vcp) |
687 | { |
688 | struct smb_connobj *cp = VCTOCP(vcp); |
689 | |
690 | mutex_enter(&cp->co_interlock); |
691 | smb_co_unlock(cp); |
692 | mutex_exit(&cp->co_interlock); |
693 | } |
694 | |
695 | |
696 | int |
697 | smb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode) |
698 | { |
699 | kauth_cred_t cred = scred->scr_cred; |
700 | int error; |
701 | |
702 | error = kauth_authorize_network(cred, KAUTH_NETWORK_SMB, |
703 | KAUTH_REQ_NETWORK_SMB_VC_ACCESS, vcp, KAUTH_ARG(mode), NULL); |
704 | if (error) |
705 | return EACCES; |
706 | |
707 | return 0; |
708 | } |
709 | |
710 | static int |
711 | smb_vc_cmpshare(struct smb_share *ssp, struct smb_sharespec *dp) |
712 | { |
713 | int exact = 1; |
714 | |
715 | if (strcmp(ssp->ss_name, dp->name) != 0) |
716 | return 1; |
717 | if (dp->owner != SMBM_ANY_OWNER) { |
718 | if (ssp->ss_uid != dp->owner) |
719 | return 1; |
720 | } else |
721 | exact = 0; |
722 | if (dp->group != SMBM_ANY_GROUP) { |
723 | if (ssp->ss_grp != dp->group) |
724 | return 1; |
725 | } else |
726 | exact = 0; |
727 | |
728 | if (dp->mode & SMBM_EXACT) { |
729 | if (!exact) |
730 | return 1; |
731 | return (dp->mode & SMBM_MASK) == ssp->ss_mode ? 0 : 1; |
732 | } |
733 | if (smb_share_access(ssp, dp->scred, dp->mode) != 0) |
734 | return 1; |
735 | return 0; |
736 | } |
737 | |
738 | /* |
739 | * Lookup share in the given VC. Share referenced and locked on return. |
740 | * VC expected to be locked on entry and will be left locked on exit. |
741 | */ |
742 | int |
743 | smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *dp, |
744 | struct smb_cred *scred, struct smb_share **sspp) |
745 | { |
746 | struct smb_connobj *osp; |
747 | struct smb_share *ssp = NULL; |
748 | int error; |
749 | |
750 | *sspp = NULL; |
751 | dp->scred = scred; |
752 | SMBCO_FOREACH(osp, VCTOCP(vcp)) { |
753 | ssp = (struct smb_share *)osp; |
754 | error = smb_share_lock(ssp); |
755 | if (error) |
756 | continue; |
757 | if (smb_vc_cmpshare(ssp, dp) == 0) |
758 | break; |
759 | smb_share_unlock(ssp); |
760 | } |
761 | if (ssp) { |
762 | smb_share_ref(ssp); |
763 | *sspp = ssp; |
764 | error = 0; |
765 | } else |
766 | error = ENOENT; |
767 | return error; |
768 | } |
769 | |
770 | int |
771 | smb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred) |
772 | { |
773 | |
774 | return smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL); |
775 | } |
776 | |
777 | /* |
778 | * Destroy VC to server, invalidate shares linked with it. |
779 | * Transport should be locked on entry. |
780 | */ |
781 | int |
782 | smb_vc_disconnect(struct smb_vc *vcp) |
783 | { |
784 | |
785 | smb_iod_request(vcp->vc_iod, SMBIOD_EV_DISCONNECT | SMBIOD_EV_SYNC, NULL); |
786 | return 0; |
787 | } |
788 | |
789 | static const char * const smb_emptypass = "" ; |
790 | |
791 | const char * |
792 | smb_vc_getpass(struct smb_vc *vcp) |
793 | { |
794 | if (vcp->vc_pass) |
795 | return vcp->vc_pass; |
796 | return smb_emptypass; |
797 | } |
798 | |
799 | |
800 | u_short |
801 | smb_vc_nextmid(struct smb_vc *vcp) |
802 | { |
803 | u_short r; |
804 | |
805 | mutex_enter(&vcp->obj.co_interlock); |
806 | r = vcp->vc_mid++; |
807 | mutex_exit(&vcp->obj.co_interlock); |
808 | return r; |
809 | } |
810 | |
811 | /* |
812 | * Share implementation |
813 | */ |
814 | /* |
815 | * Allocate share structure and attach it to the given VC |
816 | * Connection expected to be locked on entry. Share will be returned |
817 | * in locked state. |
818 | */ |
819 | int |
820 | smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, |
821 | struct smb_cred *scred, struct smb_share **sspp) |
822 | { |
823 | struct smb_share *ssp; |
824 | kauth_cred_t cred = scred->scr_cred; |
825 | uid_t realuid; |
826 | uid_t uid = shspec->owner; |
827 | gid_t gid = shspec->group; |
828 | int error; |
829 | |
830 | error = kauth_authorize_network(cred, KAUTH_NETWORK_SMB, |
831 | KAUTH_REQ_NETWORK_SMB_SHARE_CREATE, shspec, NULL, NULL); |
832 | if (error) |
833 | return EPERM; |
834 | |
835 | realuid = kauth_cred_geteuid(cred); |
836 | |
837 | error = smb_vc_lookupshare(vcp, shspec, scred, &ssp); |
838 | if (!error) { |
839 | smb_share_put(ssp, scred); |
840 | return EEXIST; |
841 | } |
842 | if (uid == SMBM_ANY_OWNER) |
843 | uid = realuid; |
844 | if (gid == SMBM_ANY_GROUP) |
845 | gid = kauth_cred_group(cred, 0); |
846 | ssp = smb_zmalloc(sizeof(*ssp), M_SMBCONN, M_WAITOK); |
847 | smb_co_init(SSTOCP(ssp), SMBL_SHARE, "smbss" ); |
848 | ssp->obj.co_free = smb_share_free; |
849 | ssp->obj.co_gone = smb_share_gone; |
850 | smb_sl_init(&ssp->ss_stlock, "ssstlock" ); |
851 | ssp->ss_name = smb_strdup(shspec->name); |
852 | if (shspec->pass && shspec->pass[0]) |
853 | ssp->ss_pass = smb_strdup(shspec->pass); |
854 | ssp->ss_type = shspec->stype; |
855 | ssp->ss_tid = SMB_TID_UNKNOWN; |
856 | ssp->ss_uid = uid; |
857 | ssp->ss_grp = gid; |
858 | ssp->ss_mode = shspec->rights & SMBM_MASK; |
859 | smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp)); |
860 | *sspp = ssp; |
861 | return 0; |
862 | } |
863 | |
864 | static void |
865 | smb_share_free(struct smb_connobj *cp) |
866 | { |
867 | struct smb_share *ssp = CPTOSS(cp); |
868 | |
869 | SMB_STRFREE(ssp->ss_name); |
870 | SMB_STRFREE(ssp->ss_pass); |
871 | smb_sl_destroy(&ssp->ss_stlock); |
872 | smb_co_done(SSTOCP(ssp)); |
873 | free(ssp, M_SMBCONN); |
874 | } |
875 | |
876 | static void |
877 | smb_share_gone(struct smb_connobj *cp, struct smb_cred *scred) |
878 | { |
879 | struct smb_share *ssp = CPTOSS(cp); |
880 | |
881 | smb_smb_treedisconnect(ssp, scred); |
882 | } |
883 | |
884 | void |
885 | smb_share_ref(struct smb_share *ssp) |
886 | { |
887 | smb_co_ref(SSTOCP(ssp)); |
888 | } |
889 | |
890 | void |
891 | smb_share_rele(struct smb_share *ssp, struct smb_cred *scred) |
892 | { |
893 | smb_co_rele(SSTOCP(ssp), scred); |
894 | } |
895 | |
896 | int |
897 | smb_share_get(struct smb_share *ssp, struct smb_cred *scred) |
898 | { |
899 | struct smb_connobj *cp = SSTOCP(ssp); |
900 | int error; |
901 | |
902 | mutex_enter(&cp->co_interlock); |
903 | error = smb_co_get(cp, scred); |
904 | mutex_exit(&cp->co_interlock); |
905 | |
906 | return error; |
907 | } |
908 | |
909 | void |
910 | smb_share_put(struct smb_share *ssp, struct smb_cred *scred) |
911 | { |
912 | smb_co_put(SSTOCP(ssp), scred); |
913 | } |
914 | |
915 | int |
916 | smb_share_lock(struct smb_share *ssp) |
917 | { |
918 | struct smb_connobj *cp = SSTOCP(ssp); |
919 | int error; |
920 | |
921 | mutex_enter(&cp->co_interlock); |
922 | error = smb_co_lock(cp); |
923 | mutex_exit(&cp->co_interlock); |
924 | |
925 | return error; |
926 | } |
927 | |
928 | void |
929 | smb_share_unlock(struct smb_share *ssp) |
930 | { |
931 | struct smb_connobj *cp = SSTOCP(ssp); |
932 | |
933 | mutex_enter(&cp->co_interlock); |
934 | smb_co_unlock(cp); |
935 | mutex_exit(&cp->co_interlock); |
936 | } |
937 | |
938 | int |
939 | smb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode) |
940 | { |
941 | kauth_cred_t cred = scred->scr_cred; |
942 | int error; |
943 | |
944 | error = kauth_authorize_network(cred, KAUTH_NETWORK_SMB, |
945 | KAUTH_REQ_NETWORK_SMB_SHARE_ACCESS, ssp, KAUTH_ARG(mode), NULL); |
946 | if (error) |
947 | return EACCES; |
948 | |
949 | return 0; |
950 | } |
951 | |
952 | int |
953 | smb_share_valid(struct smb_share *ssp) |
954 | { |
955 | return ssp->ss_tid != SMB_TID_UNKNOWN && |
956 | ssp->ss_vcgenid == SSTOVC(ssp)->vc_genid; |
957 | } |
958 | |
959 | const char* |
960 | smb_share_getpass(struct smb_share *ssp) |
961 | { |
962 | struct smb_vc *vcp; |
963 | |
964 | if (ssp->ss_pass) |
965 | return ssp->ss_pass; |
966 | vcp = SSTOVC(ssp); |
967 | if (vcp->vc_pass) |
968 | return vcp->vc_pass; |
969 | return smb_emptypass; |
970 | } |
971 | |
972 | |
973 | |