2 * Copyright (c) 1999, Boris Popov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
41 #include <sys/sysctl.h>
43 #include <netncp/ncp.h>
44 #include <netncp/ncp_subr.h>
45 #include <netncp/ncp_conn.h>
47 SLIST_HEAD(ncp_handle_head, struct ncp_handle);
49 int ncp_burst_enabled = 1;
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;
56 struct ncp_handle_head lhlist={NULL};
57 static int ncp_next_handle = 1;
58 static struct lock lhlock;
60 static int ncp_sysctl_connstat SYSCTL_HANDLER_ARGS;
61 static int ncp_conn_lock_any(struct ncp_conn *conn, struct proc *p,
64 extern struct linker_set sysctl_net_ncp;
66 SYSCTL_DECL(_net_ncp);
67 SYSCTL_NODE(_net, OID_AUTO, ncp, CTLFLAG_RW, NULL, "NetWare requester");
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");
73 MALLOC_DEFINE(M_NCPDATA, "NCP data", "NCP private data");
77 lockinit(&listlock, PSOCK, "ncpll", 0, 0);
78 lockinit(&lhlock, PSOCK, "ncplh", 0, 0);
83 ncp_conn_locklist(int flags, struct proc *p){
84 return lockmgr(&listlock, flags | LK_CANRECURSE, 0, p);
88 ncp_conn_unlocklist(struct proc *p){
89 lockmgr(&listlock, LK_RELEASE, 0, p);
93 ncp_conn_access(struct ncp_conn *conn, struct ucred *cred, mode_t mode) {
96 if (cred == NOCRED || ncp_suser(cred) == 0 ||
97 cred->cr_uid == conn->nc_owner->cr_uid)
100 if (!groupmember(conn->nc_group, cred))
102 error = (conn->li.access_mode & mode) == mode ? 0 : EACCES;
107 ncp_conn_lock_any(struct ncp_conn *conn, struct proc *p, struct ucred *cred) {
110 if (conn->nc_id == 0) return EACCES;
111 error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE, 0, p);
112 if (error == ERESTART)
114 error = ncp_chkintr(conn, p);
116 lockmgr(&conn->nc_lock, LK_RELEASE, 0, p);
120 if (conn->nc_id == 0) {
121 lockmgr(&conn->nc_lock, LK_RELEASE, 0, p);
124 conn->procp = p; /* who currently operates */
130 ncp_conn_lock(struct ncp_conn *conn, struct proc *p, struct ucred *cred, int mode) {
133 error = ncp_conn_access(conn,cred,mode);
134 if (error) return error;
135 return ncp_conn_lock_any(conn, p, cred);
139 * Lock conn but unlock connlist
142 ncp_conn_lock2(struct ncp_conn *conn, struct proc *p, struct ucred *cred, int mode) {
145 error = ncp_conn_access(conn,cred,mode);
147 ncp_conn_unlocklist(p);
151 ncp_conn_unlocklist(p);
152 error = ncp_conn_lock_any(conn,p,cred);
154 if (conn->nc_lwant == 0) {
155 wakeup(&conn->nc_lwant);
161 ncp_conn_unlock(struct ncp_conn *conn, struct proc *p) {
163 * note, that LK_RELASE will do wakeup() instead of wakeup_one().
164 * this will do a little overhead
166 lockmgr(&conn->nc_lock, LK_RELEASE, 0, p);
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);
177 * create, fill with defaults and return in locked state
180 ncp_conn_alloc(struct proc *p, struct ucred *cred, struct ncp_conn **conn)
183 struct ncp_conn *ncp;
185 MALLOC(ncp, struct ncp_conn *, sizeof(struct ncp_conn),
186 M_NCPDATA, M_WAITOK);
187 if (ncp == NULL) return ENOMEM;
189 bzero(ncp,sizeof(*ncp));
190 lockinit(&ncp->nc_lock, PZERO, "ncplck", 0, 0);
192 ncp->nc_id = ncp_next_ref++;
193 ncp->nc_owner = cred;
195 ncp->connid = 0xFFFF;
196 ncp_conn_lock_any(ncp, p, ncp->nc_owner);
198 ncp_conn_locklist(LK_EXCLUSIVE, p);
199 SLIST_INSERT_HEAD(&conn_list,ncp,nc_next);
200 ncp_conn_unlocklist(p);
205 * Remove the connection, on entry it must be locked
208 ncp_conn_free(struct ncp_conn *ncp) {
210 struct ncp_conn *ncp1;
212 if (ncp->nc_id == 0) {
213 printf("already!!!!\n");
217 NCPFATAL("conn==NULL !\n");
220 error = ncp_conn_assert_locked(ncp, __FUNCTION__, ncp->procp);
221 if (error) return error;
223 NCPFATAL("there are %d referenses left\n",ncp->ref_cnt);
227 * Mark conn as died and wait for other process
230 ncp_conn_unlock(ncp,ncp->procp);
232 * if signal is raised - how I do react ?
234 lockmgr(&ncp->nc_lock, LK_DRAIN, 0, ncp->procp);
235 while (ncp->nc_lwant) {
236 printf("lwant = %d\n", ncp->nc_lwant);
237 tsleep(&ncp->nc_lwant, PZERO,"ncpdr",2*hz);
239 ncp_conn_locklist(LK_EXCLUSIVE, ncp->procp);
241 * It is possible, that other process destroy connection while we draining,
242 * and free it. So, we must rescan list
244 SLIST_FOREACH(ncp1, &conn_list, nc_next) {
245 if (ncp1 == ncp) break;
248 ncp_conn_unlocklist(ncp->procp);
251 SLIST_REMOVE(&conn_list, ncp, struct ncp_conn, nc_next);
253 ncp_conn_unlocklist(ncp->procp);
254 if (ncp->li.user) free(ncp->li.user, M_NCPDATA);
255 if (ncp->li.password) free(ncp->li.password, M_NCPDATA);
256 crfree(ncp->nc_owner);
257 FREE(ncp, M_NCPDATA);
262 * Lookup connection by handle, return a locked conn descriptor
265 ncp_conn_getbyref(int ref,struct proc *p,struct ucred *cred, int mode, struct ncp_conn **connpp){
266 struct ncp_conn *ncp;
269 ncp_conn_locklist(LK_SHARED, p);
270 SLIST_FOREACH(ncp, &conn_list, nc_next)
271 if (ncp->nc_id == ref) break;
273 ncp_conn_unlocklist(p);
276 error = ncp_conn_lock2(ncp, p, cred, mode);
282 * find attached, but not logged in connection to specified server
285 ncp_conn_getattached(struct ncp_conn_args *li,struct proc *p,struct ucred *cred,int mode, struct ncp_conn **connpp){
286 struct ncp_conn *ncp, *ncp2=NULL;
289 ncp_conn_locklist(LK_SHARED, p);
290 SLIST_FOREACH(ncp, &conn_list, nc_next) {
291 if ((ncp->flags & NCPFL_LOGGED) != 0 ||
292 strcmp(ncp->li.server,li->server) != 0 ||
293 ncp->li.saddr.sa_len != li->saddr.sa_len ||
294 bcmp(&ncp->li.saddr,&ncp->li.saddr,li->saddr.sa_len) != 0)
296 if (ncp_suser(cred) == 0 ||
297 cred->cr_uid == ncp->nc_owner->cr_uid)
299 error = ncp_conn_access(ncp,cred,mode);
300 if (!error && ncp2 == NULL)
303 if (ncp == NULL) ncp = ncp2;
305 ncp_conn_unlocklist(p);
308 error = ncp_conn_lock2(ncp,p,cred,mode);
315 * Lookup connection by server/user pair, return a locked conn descriptor.
316 * if li is NULL or server/user pair incomplete, try to select best connection
318 * Connection selected in next order:
319 * 1. Try to search conn with ucred owner, if li is NULL also find a primary
320 * 2. If 1. fails try to get first suitable shared connection
321 * 3. If 2. fails then nothing can help to poor ucred owner
325 ncp_conn_getbyli(struct ncp_conn_args *li,struct proc *p,struct ucred *cred,int mode, struct ncp_conn **connpp){
326 struct ncp_conn *ncp, *ncp2=NULL;
327 int error=0, partial, haveserv;
329 partial = (li == NULL || li->server[0] == 0 || li->user == NULL);
330 haveserv = (li && li->server[0]);
331 ncp_conn_locklist(LK_SHARED, p);
332 SLIST_FOREACH(ncp, &conn_list, nc_next) {
334 if (cred->cr_uid == ncp->nc_owner->cr_uid) {
336 if (strcmp(ncp->li.server,li->server) == 0)
339 if (ncp->flags & NCPFL_PRIMARY)
346 if (strcmp(ncp->li.server,li->server) != 0 ||
347 ncp->li.user == NULL ||
348 strcmp(ncp->li.user,li->user) != 0)
350 if (cred->cr_uid == ncp->nc_owner->cr_uid)
352 if (ncp_suser(cred) == 0)
355 error = ncp_conn_access(ncp,cred,mode);
356 if (!error && ncp2 == NULL)
359 if (ncp == NULL) ncp = ncp2;
361 ncp_conn_unlocklist(p);
364 error = ncp_conn_lock2(ncp,p,cred,mode);
371 * Set primary connection flag, since it have sence only for an owner,
372 * only owner can modify this flag.
373 * connection expected to be locked.
376 ncp_conn_setprimary(struct ncp_conn *conn, int on){
377 struct ncp_conn *ncp=NULL;
379 if (conn->ucred->cr_uid != conn->nc_owner->cr_uid)
381 ncp_conn_locklist(LK_SHARED, conn->procp);
382 SLIST_FOREACH(ncp, &conn_list, nc_next) {
383 if (conn->ucred->cr_uid == ncp->nc_owner->cr_uid)
384 ncp->flags &= ~NCPFL_PRIMARY;
386 ncp_conn_unlocklist(conn->procp);
388 conn->flags |= NCPFL_PRIMARY;
392 * Lease conn to given proc, returning unique handle
393 * problem: how locks should be applied ?
396 ncp_conn_gethandle(struct ncp_conn *conn, struct proc *p, struct ncp_handle **handle){
397 struct ncp_handle *refp;
399 lockmgr(&lhlock, LK_EXCLUSIVE, 0, p);
400 SLIST_FOREACH(refp, &lhlist, nh_next)
401 if (refp->nh_conn == conn && p == refp->nh_proc) break;
406 lockmgr(&lhlock, LK_RELEASE, 0, p);
409 MALLOC(refp,struct ncp_handle *,sizeof(struct ncp_handle),M_NCPDATA,M_WAITOK);
410 if (refp == NULL) return ENOMEM;
411 bzero(refp,sizeof(*refp));
412 SLIST_INSERT_HEAD(&lhlist,refp,nh_next);
415 refp->nh_conn = conn;
416 refp->nh_id = ncp_next_handle++;
419 lockmgr(&lhlock, LK_RELEASE, 0, p);
423 * release reference, if force - ignore refcount
426 ncp_conn_puthandle(struct ncp_handle *handle, struct proc *p, int force) {
427 struct ncp_handle *refp = handle;
429 lockmgr(&lhlock, LK_EXCLUSIVE, 0, p);
431 refp->nh_conn->ref_cnt--;
433 refp->nh_conn->ref_cnt -= refp->nh_ref;
436 if (refp->nh_ref == 0) {
437 SLIST_REMOVE(&lhlist, refp, struct ncp_handle, nh_next);
438 FREE(refp, M_NCPDATA);
440 lockmgr(&lhlock, LK_RELEASE, 0, p);
447 ncp_conn_findhandle(int connHandle, struct proc *p, struct ncp_handle **handle) {
448 struct ncp_handle *refp;
450 lockmgr(&lhlock, LK_SHARED, 0, p);
451 SLIST_FOREACH(refp, &lhlist, nh_next)
452 if (refp->nh_proc == p && refp->nh_id == connHandle) break;
453 lockmgr(&lhlock, LK_RELEASE, 0, p);
461 * Clear handles associated with specified process
464 ncp_conn_putprochandles(struct proc *p) {
465 struct ncp_handle *hp, *nhp;
468 lockmgr(&lhlock, LK_EXCLUSIVE, 0, p);
469 for (hp = lhlist.slh_first; hp; hp = nhp) {
470 nhp = hp->nh_next.sle_next;
471 if (hp->nh_proc != p) continue;
473 hp->nh_conn->ref_cnt -= hp->nh_ref;
474 SLIST_REMOVE(&lhlist, hp, struct ncp_handle, nh_next);
477 lockmgr(&lhlock, LK_RELEASE, 0, p);
481 * remove references in all possible connections,
482 * XXX - possible problem is a locked list.
485 ncp_conn_list_rm_ref(pid_t pid) {
486 struct ncp_conn *ncp;
488 ncp_conn_locklist(LK_SHARED, NULL);
489 SLIST_FOREACH(ncp, &conn_list, nc_next) {
490 ncp_conn_rm_ref(ncp,pid,1);
492 ncp_conn_unlocklist(NULL);
497 ncp_conn_getinfo(struct ncp_conn *ncp, struct ncp_conn_stat *ncs) {
498 bzero(ncs,sizeof(*ncs));
500 ncs->li.user = ncs->user;
502 strcpy(ncs->user, ncp->li.user);
503 ncs->li.password = NULL;
504 ncs->connRef = ncp->nc_id;
505 ncs->ref_cnt = ncp->ref_cnt;
506 ncs->connid = ncp->connid;
507 ncs->owner = ncp->nc_owner->cr_uid;
508 ncs->group = ncp->nc_group;
509 ncs->flags = ncp->flags;
510 ncs->buffer_size = ncp->buffer_size;
515 ncp_sysctl_connstat SYSCTL_HANDLER_ARGS {
517 struct ncp_conn_stat ncs;
518 struct ncp_conn *ncp;
519 /* struct ucred *cred = req->p->p_ucred;*/
522 ncp_conn_locklist(LK_SHARED, req->p);
523 error = SYSCTL_OUT(req, &ncp_conn_cnt, sizeof(ncp_conn_cnt));
524 SLIST_FOREACH(ncp, &conn_list, nc_next) {
526 /* I can't do conn_lock while list is locked */
529 ncp_conn_getinfo(ncp, &ncs);
531 bzero(&ncs,sizeof(ncs));
532 ncs.connRef = ncp->nc_id;
533 strcpy(ncs.li.server,"***");
536 error = SYSCTL_OUT(req, &ncs, sizeof(ncs));
538 ncp_conn_unlocklist(req->p);