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