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