efa65992fe49391eb548281dccf2e5aa9ae06e66
[dragonfly.git] / sys / netproto / ncp / ncp_conn.c
1 /*
2  * Copyright (c) 1999, 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  * $FreeBSD: src/sys/netncp/ncp_conn.c,v 1.3.2.5 2001/02/22 08:54:11 bp Exp $
32  * $DragonFly: src/sys/netproto/ncp/ncp_conn.c,v 1.10 2006/04/23 02:33:23 dillon Exp $
33  *
34  * Connection tables
35  */
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/proc.h>
41 #include <sys/lock.h>
42 #include <sys/sysctl.h>
43
44 #include "ncp.h"
45 #include "ncp_subr.h"
46 #include "ncp_conn.h"
47
48 SLIST_HEAD(ncp_handle_head,ncp_handle);
49
50 int ncp_burst_enabled = 1;
51
52 struct ncp_conn_head conn_list={NULL};
53 static int ncp_conn_cnt = 0;
54 static int ncp_next_ref = 1;
55 static struct lock listlock;
56
57 struct ncp_handle_head lhlist={NULL};
58 static int ncp_next_handle = 1;
59 static struct lock lhlock;
60
61 static int ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS);
62 static int ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td, 
63     struct ucred *cred);
64
65 SYSCTL_DECL(_net_ncp);
66 SYSCTL_INT (_net_ncp, OID_AUTO, burst_enabled, CTLFLAG_RD, &ncp_burst_enabled, 0, "");
67 SYSCTL_INT (_net_ncp, OID_AUTO, conn_cnt, CTLFLAG_RD, &ncp_conn_cnt, 0, "");
68 SYSCTL_PROC(_net_ncp, OID_AUTO, conn_stat, CTLFLAG_RD|CTLTYPE_OPAQUE,
69             NULL, 0, ncp_sysctl_connstat, "S,connstat", "Connections list");
70
71 MALLOC_DEFINE(M_NCPDATA, "NCP data", "NCP private data");
72
73 int
74 ncp_conn_init(void) {
75         lockinit(&listlock, "ncpll", 0, 0);
76         lockinit(&lhlock, "ncplh", 0, 0);
77         return 0;
78 }
79
80 int
81 ncp_conn_locklist(int flags, struct thread *td)
82 {
83         return lockmgr(&listlock, flags | LK_CANRECURSE, NULL, td);
84 }
85
86 void
87 ncp_conn_unlocklist(struct thread *td)
88 {
89         lockmgr(&listlock, LK_RELEASE, NULL, td);
90 }
91
92 int
93 ncp_conn_access(struct ncp_conn *conn, struct ucred *cred, mode_t mode)
94 {
95         int error;
96
97         if (cred == NOCRED || ncp_suser(cred) == 0 ||
98             cred->cr_uid == conn->nc_owner->cr_uid)
99                 return 0;
100         mode >>= 3;
101         if (!groupmember(conn->nc_group, cred))
102                 mode >>= 3;
103         error = (conn->li.access_mode & mode) == mode ? 0 : EACCES;
104         return error;
105 }
106
107 /*
108  * NOTE: The lockmgr() call is protected by nc_lwant
109  */
110 int
111 ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td, struct ucred *cred)
112 {
113         int error;
114
115         if (conn->nc_id == 0) 
116                 return EACCES;
117         error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE, NULL, td);
118         if (error == ERESTART)
119                 return EINTR;
120         error = ncp_chkintr(conn, td);
121         if (error) {
122                 lockmgr(&conn->nc_lock, LK_RELEASE, NULL, td);
123                 return error;
124         }
125
126         if (conn->nc_id == 0) {
127                 lockmgr(&conn->nc_lock, LK_RELEASE, NULL, td);
128                 return EACCES;
129         }
130         conn->td = td;  /* who currently operates */
131         conn->ucred = cred;
132         return 0;
133 }
134
135 /*
136  * NOTE: The lockmgr() call should be protected by nc_lwant, but it
137  * is unclear whether callers have coded this correctly.
138  */
139 int
140 ncp_conn_lock(struct ncp_conn *conn, struct thread *td,
141         struct ucred *cred, int mode)
142 {
143         int error;
144
145         error = ncp_conn_access(conn, cred, mode);
146         if (error)
147                 return error;
148         return ncp_conn_lock_any(conn, td, cred);
149 }
150
151 /*
152  * Lock conn but unlock connlist
153  */
154 static int
155 ncp_conn_lock2(struct ncp_conn *conn, struct thread *td,
156         struct ucred *cred, int mode) 
157 {
158         int error;
159
160         error = ncp_conn_access(conn, cred, mode);
161         if (error) {
162                 ncp_conn_unlocklist(td);
163                 return error;
164         }
165         conn->nc_lwant++;
166         ncp_conn_unlocklist(td);
167         error = ncp_conn_lock_any(conn, td, cred);
168         conn->nc_lwant--;
169         if (conn->nc_lwant == 0) {
170                 wakeup(&conn->nc_lwant);
171         }
172         return error;
173 }
174
175 void
176 ncp_conn_unlock(struct ncp_conn *conn, struct thread *td) {
177         /*
178          * note, that LK_RELASE will do wakeup() instead of wakeup_one().
179          * this will do a little overhead
180          */
181         lockmgr(&conn->nc_lock, LK_RELEASE, NULL, td);
182 }
183
184 int 
185 ncp_conn_assert_locked(struct ncp_conn *conn, const char *checker,
186                        struct thread *td)
187 {
188         if (conn->nc_lock.lk_flags & LK_HAVE_EXCL)
189                 return 0;
190         printf("%s: connection isn't locked!\n", checker);
191         return EIO;
192 }
193
194 /* 
195  * create, fill with defaults and return in locked state
196  */
197 int
198 ncp_conn_alloc(struct thread *td, struct ucred *cred, struct ncp_conn **conn)
199 {
200         int error;
201         struct ncp_conn *ncp;
202
203         MALLOC(ncp, struct ncp_conn *, sizeof(struct ncp_conn), 
204             M_NCPDATA, M_WAITOK | M_ZERO);
205         error = 0;
206         lockinit(&ncp->nc_lock, "ncplck", 0, 0);
207         ncp_conn_cnt++;
208         ncp->nc_id = ncp_next_ref++;
209         ncp->nc_owner = cred;
210         ncp->seq = 0;
211         ncp->connid = 0xFFFF;
212         ncp_conn_lock_any(ncp, td, ncp->nc_owner);
213         *conn = ncp;
214         ncp_conn_locklist(LK_EXCLUSIVE, td);
215         SLIST_INSERT_HEAD(&conn_list,ncp,nc_next);
216         ncp_conn_unlocklist(td);
217         return (error);
218 }
219
220 /*
221  * Remove the connection, on entry it must be locked
222  */
223 int
224 ncp_conn_free(struct ncp_conn *ncp)
225 {
226         int error;
227         struct ncp_conn *ncp1;
228
229         if (ncp == NULL) {
230                 NCPFATAL("conn==NULL !\n");
231                 return(EIO);
232         }
233         if (ncp->nc_id == 0) {
234                 printf("already!!!!\n");
235                 return EACCES;
236         }
237         error = ncp_conn_assert_locked(ncp, __func__, ncp->td);
238         if (error)
239                 return error;
240         if (ncp->ref_cnt) {
241                 NCPFATAL("there are %d referenses left\n",ncp->ref_cnt);
242                 return(EBUSY);
243         }
244
245         /*
246          * Mark conn as died and wait for other process.  This also
247          * interlocks against other ncp_conn_free() ops.
248          */
249         ncp->nc_id = 0;
250         while (ncp->nc_lwant) {
251                 ncp_conn_unlock(ncp, ncp->td);
252                 printf("lwant = %d\n", ncp->nc_lwant);
253                 tsleep(&ncp->nc_lwant, 0, "ncpdr", 2*hz);
254                 lockmgr(&ncp->nc_lock, LK_EXCLUSIVE, NULL, ncp->td);
255         }
256         ncp_conn_locklist(LK_EXCLUSIVE, ncp->td);
257         SLIST_FOREACH(ncp1, &conn_list, nc_next) {
258                 if (ncp1 == ncp)
259                         break;
260         }
261         KKASSERT(ncp1 == ncp);
262         SLIST_REMOVE(&conn_list, ncp, ncp_conn, nc_next);
263         ncp_conn_cnt--;
264         ncp_conn_unlocklist(ncp->td);
265         if (ncp->li.user) free(ncp->li.user, M_NCPDATA);
266         if (ncp->li.password) free(ncp->li.password, M_NCPDATA);
267         crfree(ncp->nc_owner);
268         FREE(ncp, M_NCPDATA);
269         return (0);
270 }
271
272 /* 
273  * Lookup connection by handle, return a locked conn descriptor 
274  */
275 int
276 ncp_conn_getbyref(int ref,struct thread *td,struct ucred *cred, int mode, struct ncp_conn **connpp){
277         struct ncp_conn *ncp;
278         int error=0;
279
280         ncp_conn_locklist(LK_SHARED, td);
281         SLIST_FOREACH(ncp, &conn_list, nc_next)
282                 if (ncp->nc_id == ref) break;
283         if (ncp == NULL) {
284                 ncp_conn_unlocklist(td);
285                 return(EBADF);
286         }
287         error = ncp_conn_lock2(ncp, td, cred, mode);
288         if (!error)
289                 *connpp = ncp;
290         return (error);
291 }
292 /*
293  * find attached, but not logged in connection to specified server
294  */
295 int
296 ncp_conn_getattached(struct ncp_conn_args *li,struct thread *td,struct ucred *cred,int mode, struct ncp_conn **connpp){
297         struct ncp_conn *ncp, *ncp2=NULL;
298         int error = 0;
299
300         ncp_conn_locklist(LK_SHARED, td);
301         SLIST_FOREACH(ncp, &conn_list, nc_next) {
302                 if ((ncp->flags & NCPFL_LOGGED) != 0 ||
303                     strcmp(ncp->li.server,li->server) != 0 || 
304                     ncp->li.saddr.sa_len != li->saddr.sa_len ||
305                     bcmp(&ncp->li.saddr,&ncp->li.saddr,li->saddr.sa_len) != 0)
306                         continue;
307                 if (ncp_suser(cred) == 0 || 
308                     cred->cr_uid == ncp->nc_owner->cr_uid)
309                         break;
310                 error = ncp_conn_access(ncp,cred,mode);
311                 if (!error && ncp2 == NULL)
312                         ncp2 = ncp;
313         }
314         if (ncp == NULL) ncp = ncp2;
315         if (ncp == NULL) {
316                 ncp_conn_unlocklist(td);
317                 return(EBADF);
318         }
319         error = ncp_conn_lock2(ncp,td,cred,mode);
320         if (!error)
321                 *connpp=ncp;
322         return (error);
323 }
324
325 /* 
326  * Lookup connection by server/user pair, return a locked conn descriptor.
327  * if li is NULL or server/user pair incomplete, try to select best connection 
328  * based on owner.
329  * Connection selected in next order:
330  * 1. Try to search conn with ucred owner, if li is NULL also find a primary
331  * 2. If 1. fails try to get first suitable shared connection
332  * 3. If 2. fails then nothing can help to poor ucred owner
333  */
334
335 int
336 ncp_conn_getbyli(struct ncp_conn_args *li, struct thread *td,
337         struct ucred *cred, int mode, struct ncp_conn **connpp)
338 {
339         struct ncp_conn *ncp, *ncp2=NULL;
340         int error=0, partial, haveserv;
341
342         partial = (li == NULL || li->server[0] == 0 || li->user == NULL);
343         haveserv = (li && li->server[0]);
344         ncp_conn_locklist(LK_SHARED, td);
345         SLIST_FOREACH(ncp, &conn_list, nc_next) {
346                 if (partial) {
347                         if (cred->cr_uid == ncp->nc_owner->cr_uid) {
348                                 if (haveserv) {
349                                         if (strcmp(ncp->li.server,li->server) == 0)
350                                                 break;
351                                 } else {
352                                         if (ncp->flags & NCPFL_PRIMARY)
353                                                 break;
354                                         ncp2 = ncp;
355                                 }
356                                 continue;
357                         }
358                 } else {
359                         if (strcmp(ncp->li.server,li->server) != 0 || 
360                             ncp->li.user == NULL ||
361                             strcmp(ncp->li.user,li->user) != 0)
362                                 continue;
363                         if (cred->cr_uid == ncp->nc_owner->cr_uid)
364                                 break;
365                         if (ncp_suser(cred) == 0)
366                                 ncp2 = ncp;
367                 }
368                 error = ncp_conn_access(ncp,cred,mode);
369                 if (!error && ncp2 == NULL)
370                         ncp2 = ncp;
371         }
372         if (ncp == NULL) ncp = ncp2;
373         if (ncp == NULL) {
374                 ncp_conn_unlocklist(td);
375                 return(EBADF);
376         }
377         error = ncp_conn_lock2(ncp,td,cred,mode);
378         if (!error)
379                 *connpp=ncp;
380         return (error);
381 }
382
383 /*
384  * Set primary connection flag, since it have sence only for an owner,
385  * only owner can modify this flag.
386  * connection expected to be locked.
387  */
388 int
389 ncp_conn_setprimary(struct ncp_conn *conn, int on){
390         struct ncp_conn *ncp=NULL;
391
392         if (conn->ucred->cr_uid != conn->nc_owner->cr_uid)
393                 return EACCES;
394         ncp_conn_locklist(LK_SHARED, conn->td);
395         SLIST_FOREACH(ncp, &conn_list, nc_next) {
396                 if (conn->ucred->cr_uid == ncp->nc_owner->cr_uid)
397                         ncp->flags &= ~NCPFL_PRIMARY;
398         }
399         ncp_conn_unlocklist(conn->td);
400         if (on)
401                 conn->flags |= NCPFL_PRIMARY;
402         return 0;
403 }
404 /* 
405  * Lease conn to given proc, returning unique handle
406  * problem: how locks should be applied ?
407  */
408 int
409 ncp_conn_gethandle(struct ncp_conn *conn, struct thread *td, struct ncp_handle **handle){
410         struct ncp_handle *refp;
411
412         lockmgr(&lhlock, LK_EXCLUSIVE, NULL, td);
413         SLIST_FOREACH(refp, &lhlist, nh_next)
414                 if (refp->nh_conn == conn && td == refp->nh_td) break;
415         if (refp) {
416                 conn->ref_cnt++;
417                 refp->nh_ref++;
418                 *handle = refp;
419                 lockmgr(&lhlock, LK_RELEASE, NULL, td);
420                 return 0;
421         }
422         MALLOC(refp,struct ncp_handle *,sizeof(struct ncp_handle),M_NCPDATA,
423             M_WAITOK | M_ZERO);
424         SLIST_INSERT_HEAD(&lhlist,refp,nh_next);
425         refp->nh_ref++;
426         refp->nh_td = td;
427         refp->nh_conn = conn;
428         refp->nh_id = ncp_next_handle++;
429         *handle = refp;
430         conn->ref_cnt++;
431         lockmgr(&lhlock, LK_RELEASE, NULL, td);
432         return 0;
433 }
434 /*
435  * release reference, if force - ignore refcount
436  */
437 int
438 ncp_conn_puthandle(struct ncp_handle *handle, struct thread *td, int force) {
439         struct ncp_handle *refp = handle;
440
441         lockmgr(&lhlock, LK_EXCLUSIVE, NULL, td);
442         refp->nh_ref--;
443         refp->nh_conn->ref_cnt--;
444         if (force) {
445                 refp->nh_conn->ref_cnt -= refp->nh_ref;
446                 refp->nh_ref = 0;
447         }
448         if (refp->nh_ref == 0) {
449                 SLIST_REMOVE(&lhlist, refp, ncp_handle, nh_next);
450                 FREE(refp, M_NCPDATA);
451         }
452         lockmgr(&lhlock, LK_RELEASE, NULL, td);
453         return 0;
454 }
455 /*
456  * find a connHandle
457  */
458 int
459 ncp_conn_findhandle(int connHandle, struct thread *td, struct ncp_handle **handle) {
460         struct ncp_handle *refp;
461
462         lockmgr(&lhlock, LK_SHARED, NULL, td);
463         SLIST_FOREACH(refp, &lhlist, nh_next)
464                 if (refp->nh_td == td && refp->nh_id == connHandle) break;
465         lockmgr(&lhlock, LK_RELEASE, NULL, td);
466         if (refp == NULL) {
467                 return EBADF;
468         }
469         *handle = refp;
470         return 0;
471 }
472 /*
473  * Clear handles associated with specified process
474  */
475 int
476 ncp_conn_putprochandles(struct thread *td) {
477         struct ncp_handle *hp, *nhp;
478         int haveone = 0;
479
480         lockmgr(&lhlock, LK_EXCLUSIVE, NULL, td);
481         for (hp = SLIST_FIRST(&lhlist); hp; hp = nhp) {
482                 nhp = SLIST_NEXT(hp, nh_next);
483                 if (hp->nh_td != td) continue;
484                 haveone = 1;
485                 hp->nh_conn->ref_cnt -= hp->nh_ref;
486                 SLIST_REMOVE(&lhlist, hp, ncp_handle, nh_next);
487                 FREE(hp, M_NCPDATA);
488         }
489         lockmgr(&lhlock, LK_RELEASE, NULL, td);
490         return haveone;
491 }
492 /*
493  * remove references in all possible connections,
494  * XXX - possible problem is a locked list.
495  */
496 /*void
497 ncp_conn_list_rm_ref(pid_t pid) {
498         struct ncp_conn *ncp;
499
500         ncp_conn_locklist(LK_SHARED, NULL);
501         SLIST_FOREACH(ncp, &conn_list, nc_next) {
502                 ncp_conn_rm_ref(ncp,pid,1);
503         }
504         ncp_conn_unlocklist(NULL);
505         return;
506 }
507 */
508 int
509 ncp_conn_getinfo(struct ncp_conn *ncp, struct ncp_conn_stat *ncs) {
510         bzero(ncs,sizeof(*ncs));
511         ncs->li = ncp->li;
512         ncs->li.user = ncs->user;
513         if (ncp->li.user)
514                 strcpy(ncs->user, ncp->li.user);
515         ncs->li.password = NULL;
516         ncs->connRef = ncp->nc_id;
517         ncs->ref_cnt = ncp->ref_cnt;
518         ncs->connid = ncp->connid;
519         ncs->owner = ncp->nc_owner->cr_uid;
520         ncs->group = ncp->nc_group;
521         ncs->flags = ncp->flags;
522         ncs->buffer_size = ncp->buffer_size;
523         return 0;
524 }
525
526 static int
527 ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS) {
528         int error;
529         struct ncp_conn_stat ncs;
530         struct ncp_conn *ncp;
531 /*      struct ucred *cred = req->p->p_ucred;*/
532
533         error = 0;
534         ncp_conn_locklist(LK_SHARED, req->td);
535         error = SYSCTL_OUT(req, &ncp_conn_cnt, sizeof(ncp_conn_cnt));
536         SLIST_FOREACH(ncp, &conn_list, nc_next) {
537                 if (error) break;
538                 /* I can't do conn_lock while list is locked */
539                 ncp->nc_lwant++;
540                 if (!error) {
541                         ncp_conn_getinfo(ncp, &ncs);
542                 } else {
543                         bzero(&ncs,sizeof(ncs));
544                         ncs.connRef = ncp->nc_id;
545                         strcpy(ncs.li.server,"***");
546                 }
547                 ncp->nc_lwant--;
548                 error = SYSCTL_OUT(req, &ncs, sizeof(ncs));
549         }
550         ncp_conn_unlocklist(req->td);
551         return(error);
552 }