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