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