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/cdefs.h>
36 __FBSDID("$FreeBSD$");
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
44 #include <sys/sysctl.h>
46 #include <netncp/ncp.h>
47 #include <netncp/nwerror.h>
48 #include <netncp/ncp_subr.h>
49 #include <netncp/ncp_conn.h>
50 #include <netncp/ncp_sock.h>
51 #include <netncp/ncp_ncp.h>
53 SLIST_HEAD(ncp_handle_head,ncp_handle);
55 int ncp_burst_enabled = 1;
57 struct ncp_conn_head conn_list={NULL};
58 static int ncp_conn_cnt = 0;
59 static int ncp_next_ref = 1;
60 static struct lock listlock;
62 struct ncp_handle_head lhlist={NULL};
63 static int ncp_next_handle = 1;
64 static struct lock lhlock;
66 static int ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS);
67 static int ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td,
70 SYSCTL_DECL(_net_ncp);
71 SYSCTL_INT (_net_ncp, OID_AUTO, burst_enabled, CTLFLAG_RD, &ncp_burst_enabled, 0, "");
72 SYSCTL_INT (_net_ncp, OID_AUTO, conn_cnt, CTLFLAG_RD, &ncp_conn_cnt, 0, "");
73 SYSCTL_PROC(_net_ncp, OID_AUTO, conn_stat, CTLFLAG_RD|CTLTYPE_OPAQUE,
74 NULL, 0, ncp_sysctl_connstat, "S,connstat", "Connections list");
76 MALLOC_DEFINE(M_NCPDATA, "NCP data", "NCP private data");
81 lockinit(&listlock, PSOCK, "ncpll", 0, 0);
82 lockinit(&lhlock, PSOCK, "ncplh", 0, 0);
87 ncp_conn_destroy(void)
90 NCPERROR("There are %d connections active\n", ncp_conn_cnt);
93 lockdestroy(&listlock);
99 ncp_conn_locklist(int flags, struct thread *td)
101 return lockmgr(&listlock, flags | LK_CANRECURSE, 0, td);
105 ncp_conn_unlocklist(struct thread *td)
107 lockmgr(&listlock, LK_RELEASE, 0, td);
111 ncp_conn_access(struct ncp_conn *conn, struct ucred *cred, mode_t mode)
115 if (cred == NOCRED || ncp_suser(cred) == 0 ||
116 cred->cr_uid == conn->nc_owner->cr_uid)
119 if (!groupmember(conn->nc_group, cred))
121 error = (conn->li.access_mode & mode) == mode ? 0 : EACCES;
126 ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td, struct ucred *cred)
130 if (conn->nc_id == 0) return EACCES;
131 error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE, 0, td);
132 if (error == ERESTART)
134 error = ncp_chkintr(conn, td);
136 lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
140 if (conn->nc_id == 0) {
141 lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
144 conn->td = td; /* who currently operates */
150 ncp_conn_lock(struct ncp_conn *conn, struct thread *td, struct ucred *cred, int mode)
154 error = ncp_conn_access(conn, cred, mode);
155 if (error) return error;
156 return ncp_conn_lock_any(conn, td, cred);
160 * Lock conn but unlock connlist
163 ncp_conn_lock2(struct ncp_conn *conn, struct thread *td, struct ucred *cred, int mode)
167 error = ncp_conn_access(conn, cred, mode);
169 ncp_conn_unlocklist(td);
173 ncp_conn_unlocklist(td);
174 error = ncp_conn_lock_any(conn, td, cred);
176 if (conn->nc_lwant == 0) {
177 wakeup(&conn->nc_lwant);
183 ncp_conn_unlock(struct ncp_conn *conn, struct thread *td)
186 * note, that LK_RELASE will do wakeup() instead of wakeup_one().
187 * this will do a little overhead
189 lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
193 ncp_conn_assert_locked(struct ncp_conn *conn, const char *checker, struct thread *td)
195 if (conn->nc_lock.lk_flags & LK_HAVE_EXCL) return 0;
196 printf("%s: connection isn't locked!\n", checker);
201 ncp_conn_invalidate(struct ncp_conn *ncp)
203 ncp->flags &= ~(NCPFL_ATTACHED | NCPFL_LOGGED | NCPFL_INVALID);
207 ncp_conn_invalid(struct ncp_conn *ncp)
209 return ncp->flags & NCPFL_INVALID;
213 * create, fill with defaults and return in locked state
216 ncp_conn_alloc(struct ncp_conn_args *cap, struct thread *td, struct ucred *cred,
217 struct ncp_conn **conn)
219 struct ncp_conn *ncp;
223 if (cap->saddr.sa_family != AF_INET && cap->saddr.sa_family != AF_IPX)
224 return EPROTONOSUPPORT;
225 isroot = ncp_suser(cred) == 0;
227 * Only root can change ownership
229 if (cap->owner != NCP_DEFAULT_OWNER && !isroot)
231 if (cap->group != NCP_DEFAULT_GROUP &&
232 !groupmember(cap->group, cred) && !isroot)
234 if (cap->owner != NCP_DEFAULT_OWNER) {
236 owner->cr_uid = cap->owner;
238 owner = crhold(cred);
239 MALLOC(ncp, struct ncp_conn *, sizeof(struct ncp_conn),
240 M_NCPDATA, M_WAITOK | M_ZERO);
242 lockinit(&ncp->nc_lock, PZERO, "ncplck", 0, 0);
244 ncp->nc_id = ncp_next_ref++;
245 ncp->nc_owner = cred;
247 ncp->connid = 0xFFFF;
249 ncp->nc_group = (cap->group != NCP_DEFAULT_GROUP) ?
250 cap->group : cred->cr_groups[0];
252 if (cap->retry_count == 0)
253 ncp->li.retry_count = NCP_RETRY_COUNT;
254 if (cap->timeout == 0)
255 ncp->li.timeout = NCP_RETRY_TIMEOUT;
256 ncp_conn_lock_any(ncp, td, ncp->nc_owner);
258 ncp_conn_locklist(LK_EXCLUSIVE, td);
259 SLIST_INSERT_HEAD(&conn_list,ncp,nc_next);
260 ncp_conn_unlocklist(td);
265 * Remove the connection, on entry it must be locked
268 ncp_conn_free(struct ncp_conn *ncp)
274 NCPFATAL("ncp == NULL\n");
277 if (ncp->nc_id == 0) {
278 NCPERROR("nc_id == 0\n");
282 error = ncp_conn_assert_locked(ncp, __func__, td);
285 if (ncp->ref_cnt != 0 || (ncp->flags & NCPFL_PERMANENT))
287 if (ncp_conn_access(ncp, ncp->ucred, NCPM_WRITE))
290 if (ncp->flags & NCPFL_ATTACHED)
291 ncp_ncp_disconnect(ncp);
292 ncp_sock_disconnect(ncp);
295 * Mark conn as dead and wait for other process
298 ncp_conn_unlock(ncp, td);
300 * if signal is raised - how I do react ?
302 lockmgr(&ncp->nc_lock, LK_DRAIN, 0, td);
303 lockdestroy(&ncp->nc_lock);
304 while (ncp->nc_lwant) {
305 printf("lwant = %d\n", ncp->nc_lwant);
306 tsleep(&ncp->nc_lwant, PZERO,"ncpdr",2*hz);
308 ncp_conn_locklist(LK_EXCLUSIVE, td);
309 SLIST_REMOVE(&conn_list, ncp, ncp_conn, nc_next);
311 ncp_conn_unlocklist(td);
313 free(ncp->li.user, M_NCPDATA);
314 if (ncp->li.password)
315 free(ncp->li.password, M_NCPDATA);
316 crfree(ncp->nc_owner);
317 FREE(ncp, M_NCPDATA);
322 ncp_conn_reconnect(struct ncp_conn *ncp)
327 * Close opened sockets if any
329 ncp_sock_disconnect(ncp);
330 error = ncp_sock_connect(ncp);
333 error = ncp_ncp_connect(ncp);
336 error = ncp_renegotiate_connparam(ncp, NCP_DEFAULT_BUFSIZE, 0);
337 if (error == NWE_SIGNATURE_LEVEL_CONFLICT) {
338 printf("Unable to negotiate requested security level\n");
342 ncp_ncp_disconnect(ncp);
346 error = ncp_burst_connect(ncp);
348 ncp_ncp_disconnect(ncp);
356 ncp_conn_login(struct ncp_conn *conn, struct thread *td, struct ucred *cred)
358 struct ncp_bindery_object user;
362 error = ncp_get_encryption_key(conn, ncp_key);
364 printf("%s: Warning: use unencrypted login\n", __func__);
365 error = ncp_login_unencrypted(conn, conn->li.objtype,
366 conn->li.user, conn->li.password, td, cred);
368 error = ncp_get_bindery_object_id(conn, conn->li.objtype,
369 conn->li.user, &user, td, cred);
372 error = ncp_login_encrypted(conn, &user, ncp_key,
373 conn->li.password, td, cred);
376 conn->flags |= NCPFL_LOGGED | NCPFL_WASLOGGED;
381 * Lookup connection by handle, return a locked conn descriptor
384 ncp_conn_getbyref(int ref, struct thread *td, struct ucred *cred, int mode,
385 struct ncp_conn **connpp)
387 struct ncp_conn *ncp;
390 ncp_conn_locklist(LK_SHARED, td);
391 SLIST_FOREACH(ncp, &conn_list, nc_next)
392 if (ncp->nc_id == ref) break;
394 ncp_conn_unlocklist(td);
397 error = ncp_conn_lock2(ncp, td, cred, mode);
403 * find attached, but not logged in connection to specified server
406 ncp_conn_getattached(struct ncp_conn_args *li, struct thread *td,
407 struct ucred *cred, int mode, struct ncp_conn **connpp)
409 struct ncp_conn *ncp, *ncp2 = NULL;
412 ncp_conn_locklist(LK_SHARED, td);
413 SLIST_FOREACH(ncp, &conn_list, nc_next) {
414 if ((ncp->flags & NCPFL_LOGGED) != 0 ||
415 strcmp(ncp->li.server,li->server) != 0 ||
416 ncp->li.saddr.sa_len != li->saddr.sa_len ||
417 bcmp(&ncp->li.saddr,&ncp->li.saddr,li->saddr.sa_len) != 0)
419 if (ncp_suser(cred) == 0 ||
420 cred->cr_uid == ncp->nc_owner->cr_uid)
422 error = ncp_conn_access(ncp,cred,mode);
423 if (!error && ncp2 == NULL)
426 if (ncp == NULL) ncp = ncp2;
428 ncp_conn_unlocklist(td);
431 error = ncp_conn_lock2(ncp, td, cred, mode);
438 * Lookup connection by server/user pair, return a locked conn descriptor.
439 * if li is NULL or server/user pair incomplete, try to select best connection
441 * Connection selected in next order:
442 * 1. Try to search conn with ucred owner, if li is NULL also find a primary
443 * 2. If 1. fails try to get first suitable shared connection
444 * 3. If 2. fails then nothing can help to poor ucred owner
448 ncp_conn_getbyli(struct ncp_conn_args *li, struct thread *td,
449 struct ucred *cred, int mode, struct ncp_conn **connpp)
451 struct ncp_conn *ncp, *ncp2 = NULL;
452 int error = 0, partial, haveserv;
454 partial = (li == NULL || li->server[0] == 0 || li->user == NULL);
455 haveserv = (li && li->server[0]);
456 ncp_conn_locklist(LK_SHARED, td);
457 SLIST_FOREACH(ncp, &conn_list, nc_next) {
459 if (cred->cr_uid == ncp->nc_owner->cr_uid) {
461 if (strcmp(ncp->li.server,li->server) == 0)
464 if (ncp->flags & NCPFL_PRIMARY)
471 if (strcmp(ncp->li.server,li->server) != 0 ||
472 ncp->li.user == NULL ||
473 strcmp(ncp->li.user,li->user) != 0)
475 if (cred->cr_uid == ncp->nc_owner->cr_uid)
477 if (ncp_suser(cred) == 0)
480 error = ncp_conn_access(ncp,cred,mode);
481 if (!error && ncp2 == NULL)
484 if (ncp == NULL) ncp = ncp2;
486 ncp_conn_unlocklist(td);
489 error = ncp_conn_lock2(ncp, td, cred,mode);
496 * Set primary connection flag, since it have sence only for an owner,
497 * only owner can modify this flag.
498 * connection expected to be locked.
501 ncp_conn_setprimary(struct ncp_conn *conn, int on)
503 struct ncp_conn *ncp=NULL;
505 if (conn->ucred->cr_uid != conn->nc_owner->cr_uid)
507 ncp_conn_locklist(LK_SHARED, conn->td);
508 SLIST_FOREACH(ncp, &conn_list, nc_next) {
509 if (conn->ucred->cr_uid == ncp->nc_owner->cr_uid)
510 ncp->flags &= ~NCPFL_PRIMARY;
512 ncp_conn_unlocklist(conn->td);
514 conn->flags |= NCPFL_PRIMARY;
518 * Lease conn to given proc, returning unique handle
519 * problem: how locks should be applied ?
522 ncp_conn_gethandle(struct ncp_conn *conn, struct thread *td, struct ncp_handle **handle)
524 struct ncp_handle *refp;
526 lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
527 SLIST_FOREACH(refp, &lhlist, nh_next)
528 if (refp->nh_conn == conn && td == refp->nh_td) break;
533 lockmgr(&lhlock, LK_RELEASE, 0, td);
536 MALLOC(refp,struct ncp_handle *,sizeof(struct ncp_handle),M_NCPDATA,
538 SLIST_INSERT_HEAD(&lhlist,refp,nh_next);
541 refp->nh_conn = conn;
542 refp->nh_id = ncp_next_handle++;
545 lockmgr(&lhlock, LK_RELEASE, 0, td);
549 * release reference, if force - ignore refcount
552 ncp_conn_puthandle(struct ncp_handle *handle, struct thread *td, int force)
554 struct ncp_handle *refp = handle;
556 lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
558 refp->nh_conn->ref_cnt--;
560 refp->nh_conn->ref_cnt -= refp->nh_ref;
563 if (refp->nh_ref == 0) {
564 SLIST_REMOVE(&lhlist, refp, ncp_handle, nh_next);
565 FREE(refp, M_NCPDATA);
567 lockmgr(&lhlock, LK_RELEASE, 0, td);
574 ncp_conn_findhandle(int connHandle, struct thread *td, struct ncp_handle **handle) {
575 struct ncp_handle *refp;
577 lockmgr(&lhlock, LK_SHARED, 0, td);
578 SLIST_FOREACH(refp, &lhlist, nh_next)
579 if (refp->nh_td == td && refp->nh_id == connHandle) break;
580 lockmgr(&lhlock, LK_RELEASE, 0, td);
588 * Clear handles associated with specified process
591 ncp_conn_putprochandles(struct thread *td)
593 struct ncp_handle *hp, *nhp;
596 lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
597 for (hp = SLIST_FIRST(&lhlist); hp; hp = nhp) {
598 nhp = SLIST_NEXT(hp, nh_next);
599 if (hp->nh_td != td) continue;
601 hp->nh_conn->ref_cnt -= hp->nh_ref;
602 SLIST_REMOVE(&lhlist, hp, ncp_handle, nh_next);
605 lockmgr(&lhlock, LK_RELEASE, 0, td);
609 * remove references in all possible connections,
610 * XXX - possible problem is a locked list.
613 ncp_conn_list_rm_ref(pid_t pid) {
614 struct ncp_conn *ncp;
616 ncp_conn_locklist(LK_SHARED, NULL);
617 SLIST_FOREACH(ncp, &conn_list, nc_next) {
618 ncp_conn_rm_ref(ncp,pid,1);
620 ncp_conn_unlocklist(NULL);
625 ncp_conn_getinfo(struct ncp_conn *ncp, struct ncp_conn_stat *ncs) {
626 bzero(ncs,sizeof(*ncs));
628 ncs->li.user = ncs->user;
630 strcpy(ncs->user, ncp->li.user);
631 ncs->li.password = NULL;
632 ncs->connRef = ncp->nc_id;
633 ncs->ref_cnt = ncp->ref_cnt;
634 ncs->connid = ncp->connid;
635 ncs->owner = ncp->nc_owner->cr_uid;
636 ncs->group = ncp->nc_group;
637 ncs->flags = ncp->flags;
638 ncs->buffer_size = ncp->buffer_size;
643 ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS)
646 struct ncp_conn_stat ncs;
647 struct ncp_conn *ncp;
648 /* struct ucred *cred = req->td->td_ucred;*/
650 error = sysctl_wire_old_buffer(req, 0);
653 ncp_conn_locklist(LK_SHARED, req->td);
654 error = SYSCTL_OUT(req, &ncp_conn_cnt, sizeof(ncp_conn_cnt));
655 SLIST_FOREACH(ncp, &conn_list, nc_next) {
657 /* I can't do conn_lock while list is locked */
660 ncp_conn_getinfo(ncp, &ncs);
662 bzero(&ncs,sizeof(ncs));
663 ncs.connRef = ncp->nc_id;
664 strcpy(ncs.li.server,"***");
667 error = SYSCTL_OUT(req, &ncs, sizeof(ncs));
669 ncp_conn_unlocklist(req->td);