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>
45 #include <sys/sysctl.h>
47 #include <netncp/ncp.h>
48 #include <netncp/nwerror.h>
49 #include <netncp/ncp_subr.h>
50 #include <netncp/ncp_conn.h>
51 #include <netncp/ncp_sock.h>
52 #include <netncp/ncp_ncp.h>
54 SLIST_HEAD(ncp_handle_head
,ncp_handle
);
56 int ncp_burst_enabled
= 1;
58 struct ncp_conn_head conn_list
={NULL
};
59 static int ncp_conn_cnt
= 0;
60 static int ncp_next_ref
= 1;
61 static struct lock listlock
;
63 struct ncp_handle_head lhlist
={NULL
};
64 static int ncp_next_handle
= 1;
65 static struct lock lhlock
;
67 static int ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS
);
68 static int ncp_conn_lock_any(struct ncp_conn
*conn
, struct thread
*td
,
71 SYSCTL_DECL(_net_ncp
);
72 SYSCTL_INT (_net_ncp
, OID_AUTO
, burst_enabled
, CTLFLAG_RD
, &ncp_burst_enabled
, 0, "");
73 SYSCTL_INT (_net_ncp
, OID_AUTO
, conn_cnt
, CTLFLAG_RD
, &ncp_conn_cnt
, 0, "");
74 SYSCTL_PROC(_net_ncp
, OID_AUTO
, conn_stat
, CTLFLAG_RD
|CTLTYPE_OPAQUE
,
75 NULL
, 0, ncp_sysctl_connstat
, "S,connstat", "Connections list");
77 MALLOC_DEFINE(M_NCPDATA
, "ncp_data", "NCP private data");
82 lockinit(&listlock
, PSOCK
, "ncpll", 0, 0);
83 lockinit(&lhlock
, PSOCK
, "ncplh", 0, 0);
88 ncp_conn_destroy(void)
91 NCPERROR("There are %d connections active\n", ncp_conn_cnt
);
94 lockdestroy(&listlock
);
100 ncp_conn_locklist(int flags
, struct thread
*td
)
102 return lockmgr(&listlock
, flags
| LK_CANRECURSE
, 0);
106 ncp_conn_unlocklist(struct thread
*td
)
108 lockmgr(&listlock
, LK_RELEASE
, 0);
112 ncp_conn_access(struct ncp_conn
*conn
, struct ucred
*cred
, mode_t mode
)
116 if (cred
== NOCRED
|| ncp_suser(cred
) == 0 ||
117 cred
->cr_uid
== conn
->nc_owner
->cr_uid
)
120 if (!groupmember(conn
->nc_group
, cred
))
122 error
= (conn
->li
.access_mode
& mode
) == mode
? 0 : EACCES
;
127 ncp_conn_lock_any(struct ncp_conn
*conn
, struct thread
*td
, struct ucred
*cred
)
131 if (conn
->nc_id
== 0) return EACCES
;
132 error
= lockmgr(&conn
->nc_lock
, LK_EXCLUSIVE
| LK_CANRECURSE
, 0);
133 if (error
== ERESTART
)
135 error
= ncp_chkintr(conn
, td
);
137 lockmgr(&conn
->nc_lock
, LK_RELEASE
, 0);
141 if (conn
->nc_id
== 0) {
142 lockmgr(&conn
->nc_lock
, LK_RELEASE
, 0);
145 conn
->td
= td
; /* who currently operates */
151 ncp_conn_lock(struct ncp_conn
*conn
, struct thread
*td
, struct ucred
*cred
, int mode
)
155 error
= ncp_conn_access(conn
, cred
, mode
);
156 if (error
) return error
;
157 return ncp_conn_lock_any(conn
, td
, cred
);
161 * Lock conn but unlock connlist
164 ncp_conn_lock2(struct ncp_conn
*conn
, struct thread
*td
, struct ucred
*cred
, int mode
)
168 error
= ncp_conn_access(conn
, cred
, mode
);
170 ncp_conn_unlocklist(td
);
174 ncp_conn_unlocklist(td
);
175 error
= ncp_conn_lock_any(conn
, td
, cred
);
177 if (conn
->nc_lwant
== 0) {
178 wakeup(&conn
->nc_lwant
);
184 ncp_conn_unlock(struct ncp_conn
*conn
, struct thread
*td
)
187 * note, that LK_RELASE will do wakeup() instead of wakeup_one().
188 * this will do a little overhead
190 lockmgr(&conn
->nc_lock
, LK_RELEASE
, 0);
194 ncp_conn_assert_locked(struct ncp_conn
*conn
, const char *checker
, struct thread
*td
)
196 if (lockstatus(&conn
->nc_lock
) == LK_EXCLUSIVE
) return 0;
197 printf("%s: connection isn't locked!\n", checker
);
202 ncp_conn_invalidate(struct ncp_conn
*ncp
)
204 ncp
->flags
&= ~(NCPFL_ATTACHED
| NCPFL_LOGGED
| NCPFL_INVALID
);
208 ncp_conn_invalid(struct ncp_conn
*ncp
)
210 return ncp
->flags
& NCPFL_INVALID
;
214 * create, fill with defaults and return in locked state
217 ncp_conn_alloc(struct ncp_conn_args
*cap
, struct thread
*td
, struct ucred
*cred
,
218 struct ncp_conn
**conn
)
220 struct ncp_conn
*ncp
;
224 if (cap
->saddr
.sa_family
!= AF_INET
&& cap
->saddr
.sa_family
!= AF_IPX
)
225 return EPROTONOSUPPORT
;
227 * Only root can change ownership.
229 isroot
= ncp_suser(cred
) == 0;
230 if (cap
->owner
!= NCP_DEFAULT_OWNER
&& !isroot
)
232 if (cap
->group
!= NCP_DEFAULT_GROUP
&&
233 !groupmember(cap
->group
, cred
) && !isroot
)
235 if (cap
->owner
!= NCP_DEFAULT_OWNER
) {
238 owner
->cr_uid
= cap
->owner
;
240 owner
= crhold(cred
);
241 MALLOC(ncp
, struct ncp_conn
*, sizeof(struct ncp_conn
),
242 M_NCPDATA
, M_WAITOK
| M_ZERO
);
244 lockinit(&ncp
->nc_lock
, PZERO
, "ncplck", 0, 0);
246 ncp
->nc_id
= ncp_next_ref
++;
247 ncp
->nc_owner
= owner
;
249 ncp
->connid
= 0xFFFF;
251 ncp
->nc_group
= (cap
->group
!= NCP_DEFAULT_GROUP
) ?
252 cap
->group
: cred
->cr_groups
[0];
254 if (cap
->retry_count
== 0)
255 ncp
->li
.retry_count
= NCP_RETRY_COUNT
;
256 if (cap
->timeout
== 0)
257 ncp
->li
.timeout
= NCP_RETRY_TIMEOUT
;
258 ncp_conn_lock_any(ncp
, td
, ncp
->nc_owner
);
260 ncp_conn_locklist(LK_EXCLUSIVE
, td
);
261 SLIST_INSERT_HEAD(&conn_list
,ncp
,nc_next
);
262 ncp_conn_unlocklist(td
);
267 * Remove the connection, on entry it must be locked
270 ncp_conn_free(struct ncp_conn
*ncp
)
276 NCPFATAL("ncp == NULL\n");
279 if (ncp
->nc_id
== 0) {
280 NCPERROR("nc_id == 0\n");
284 error
= ncp_conn_assert_locked(ncp
, __func__
, td
);
287 if (ncp
->ref_cnt
!= 0 || (ncp
->flags
& NCPFL_PERMANENT
))
289 if (ncp_conn_access(ncp
, ncp
->ucred
, NCPM_WRITE
))
292 if (ncp
->flags
& NCPFL_ATTACHED
)
293 ncp_ncp_disconnect(ncp
);
294 ncp_sock_disconnect(ncp
);
297 * Mark conn as dead and wait for other process
300 ncp_conn_unlock(ncp
, td
);
302 * if signal is raised - how I do react ?
304 lockmgr(&ncp
->nc_lock
, LK_DRAIN
, 0);
305 lockmgr(&ncp
->nc_lock
, LK_RELEASE
, 0);
306 lockdestroy(&ncp
->nc_lock
);
307 while (ncp
->nc_lwant
) {
308 printf("lwant = %d\n", ncp
->nc_lwant
);
309 tsleep(&ncp
->nc_lwant
, PZERO
,"ncpdr",2*hz
);
311 ncp_conn_locklist(LK_EXCLUSIVE
, td
);
312 SLIST_REMOVE(&conn_list
, ncp
, ncp_conn
, nc_next
);
314 ncp_conn_unlocklist(td
);
316 free(ncp
->li
.user
, M_NCPDATA
);
317 if (ncp
->li
.password
)
318 free(ncp
->li
.password
, M_NCPDATA
);
319 crfree(ncp
->nc_owner
);
320 FREE(ncp
, M_NCPDATA
);
325 ncp_conn_reconnect(struct ncp_conn
*ncp
)
330 * Close opened sockets if any
332 ncp_sock_disconnect(ncp
);
333 error
= ncp_sock_connect(ncp
);
336 error
= ncp_ncp_connect(ncp
);
339 error
= ncp_renegotiate_connparam(ncp
, NCP_DEFAULT_BUFSIZE
, 0);
340 if (error
== NWE_SIGNATURE_LEVEL_CONFLICT
) {
341 printf("Unable to negotiate requested security level\n");
345 ncp_ncp_disconnect(ncp
);
349 error
= ncp_burst_connect(ncp
);
351 ncp_ncp_disconnect(ncp
);
359 ncp_conn_login(struct ncp_conn
*conn
, struct thread
*td
, struct ucred
*cred
)
361 struct ncp_bindery_object user
;
365 error
= ncp_get_encryption_key(conn
, ncp_key
);
367 printf("%s: Warning: use unencrypted login\n", __func__
);
368 error
= ncp_login_unencrypted(conn
, conn
->li
.objtype
,
369 conn
->li
.user
, conn
->li
.password
, td
, cred
);
371 error
= ncp_get_bindery_object_id(conn
, conn
->li
.objtype
,
372 conn
->li
.user
, &user
, td
, cred
);
375 error
= ncp_login_encrypted(conn
, &user
, ncp_key
,
376 conn
->li
.password
, td
, cred
);
379 conn
->flags
|= NCPFL_LOGGED
| NCPFL_WASLOGGED
;
384 * Lookup connection by handle, return a locked conn descriptor
387 ncp_conn_getbyref(int ref
, struct thread
*td
, struct ucred
*cred
, int mode
,
388 struct ncp_conn
**connpp
)
390 struct ncp_conn
*ncp
;
393 ncp_conn_locklist(LK_SHARED
, td
);
394 SLIST_FOREACH(ncp
, &conn_list
, nc_next
)
395 if (ncp
->nc_id
== ref
) break;
397 ncp_conn_unlocklist(td
);
400 error
= ncp_conn_lock2(ncp
, td
, cred
, mode
);
406 * find attached, but not logged in connection to specified server
409 ncp_conn_getattached(struct ncp_conn_args
*li
, struct thread
*td
,
410 struct ucred
*cred
, int mode
, struct ncp_conn
**connpp
)
412 struct ncp_conn
*ncp
, *ncp2
= NULL
;
415 ncp_conn_locklist(LK_SHARED
, td
);
416 SLIST_FOREACH(ncp
, &conn_list
, nc_next
) {
417 if ((ncp
->flags
& NCPFL_LOGGED
) != 0 ||
418 strcmp(ncp
->li
.server
,li
->server
) != 0 ||
419 ncp
->li
.saddr
.sa_len
!= li
->saddr
.sa_len
||
420 bcmp(&ncp
->li
.saddr
,&ncp
->li
.saddr
,li
->saddr
.sa_len
) != 0)
422 if (ncp_suser(cred
) == 0 ||
423 cred
->cr_uid
== ncp
->nc_owner
->cr_uid
)
425 error
= ncp_conn_access(ncp
,cred
,mode
);
426 if (!error
&& ncp2
== NULL
)
429 if (ncp
== NULL
) ncp
= ncp2
;
431 ncp_conn_unlocklist(td
);
434 error
= ncp_conn_lock2(ncp
, td
, cred
, mode
);
441 * Lookup connection by server/user pair, return a locked conn descriptor.
442 * if li is NULL or server/user pair incomplete, try to select best connection
444 * Connection selected in next order:
445 * 1. Try to search conn with ucred owner, if li is NULL also find a primary
446 * 2. If 1. fails try to get first suitable shared connection
447 * 3. If 2. fails then nothing can help to poor ucred owner
451 ncp_conn_getbyli(struct ncp_conn_args
*li
, struct thread
*td
,
452 struct ucred
*cred
, int mode
, struct ncp_conn
**connpp
)
454 struct ncp_conn
*ncp
, *ncp2
= NULL
;
455 int error
= 0, partial
, haveserv
;
457 partial
= (li
== NULL
|| li
->server
[0] == 0 || li
->user
== NULL
);
458 haveserv
= (li
&& li
->server
[0]);
459 ncp_conn_locklist(LK_SHARED
, td
);
460 SLIST_FOREACH(ncp
, &conn_list
, nc_next
) {
462 if (cred
->cr_uid
== ncp
->nc_owner
->cr_uid
) {
464 if (strcmp(ncp
->li
.server
,li
->server
) == 0)
467 if (ncp
->flags
& NCPFL_PRIMARY
)
474 if (strcmp(ncp
->li
.server
,li
->server
) != 0 ||
475 ncp
->li
.user
== NULL
||
476 strcmp(ncp
->li
.user
,li
->user
) != 0)
478 if (cred
->cr_uid
== ncp
->nc_owner
->cr_uid
)
480 if (ncp_suser(cred
) == 0)
483 error
= ncp_conn_access(ncp
,cred
,mode
);
484 if (!error
&& ncp2
== NULL
)
487 if (ncp
== NULL
) ncp
= ncp2
;
489 ncp_conn_unlocklist(td
);
492 error
= ncp_conn_lock2(ncp
, td
, cred
,mode
);
499 * Set primary connection flag, since it have sence only for an owner,
500 * only owner can modify this flag.
501 * connection expected to be locked.
504 ncp_conn_setprimary(struct ncp_conn
*conn
, int on
)
506 struct ncp_conn
*ncp
=NULL
;
508 if (conn
->ucred
->cr_uid
!= conn
->nc_owner
->cr_uid
)
510 ncp_conn_locklist(LK_SHARED
, conn
->td
);
511 SLIST_FOREACH(ncp
, &conn_list
, nc_next
) {
512 if (conn
->ucred
->cr_uid
== ncp
->nc_owner
->cr_uid
)
513 ncp
->flags
&= ~NCPFL_PRIMARY
;
515 ncp_conn_unlocklist(conn
->td
);
517 conn
->flags
|= NCPFL_PRIMARY
;
521 * Lease conn to given proc, returning unique handle
522 * problem: how locks should be applied ?
525 ncp_conn_gethandle(struct ncp_conn
*conn
, struct thread
*td
, struct ncp_handle
**handle
)
527 struct ncp_handle
*refp
;
529 lockmgr(&lhlock
, LK_EXCLUSIVE
, 0);
530 SLIST_FOREACH(refp
, &lhlist
, nh_next
)
531 if (refp
->nh_conn
== conn
&& td
== refp
->nh_td
) break;
536 lockmgr(&lhlock
, LK_RELEASE
, 0);
539 MALLOC(refp
,struct ncp_handle
*,sizeof(struct ncp_handle
),M_NCPDATA
,
541 SLIST_INSERT_HEAD(&lhlist
,refp
,nh_next
);
544 refp
->nh_conn
= conn
;
545 refp
->nh_id
= ncp_next_handle
++;
548 lockmgr(&lhlock
, LK_RELEASE
, 0);
552 * release reference, if force - ignore refcount
555 ncp_conn_puthandle(struct ncp_handle
*handle
, struct thread
*td
, int force
)
557 struct ncp_handle
*refp
= handle
;
559 lockmgr(&lhlock
, LK_EXCLUSIVE
, 0);
561 refp
->nh_conn
->ref_cnt
--;
563 refp
->nh_conn
->ref_cnt
-= refp
->nh_ref
;
566 if (refp
->nh_ref
== 0) {
567 SLIST_REMOVE(&lhlist
, refp
, ncp_handle
, nh_next
);
568 FREE(refp
, M_NCPDATA
);
570 lockmgr(&lhlock
, LK_RELEASE
, 0);
577 ncp_conn_findhandle(int connHandle
, struct thread
*td
, struct ncp_handle
**handle
) {
578 struct ncp_handle
*refp
;
580 lockmgr(&lhlock
, LK_SHARED
, 0);
581 SLIST_FOREACH(refp
, &lhlist
, nh_next
)
582 if (refp
->nh_td
== td
&& refp
->nh_id
== connHandle
) break;
583 lockmgr(&lhlock
, LK_RELEASE
, 0);
591 * Clear handles associated with specified process
594 ncp_conn_putprochandles(struct thread
*td
)
596 struct ncp_handle
*hp
, *nhp
;
599 lockmgr(&lhlock
, LK_EXCLUSIVE
, 0);
600 for (hp
= SLIST_FIRST(&lhlist
); hp
; hp
= nhp
) {
601 nhp
= SLIST_NEXT(hp
, nh_next
);
602 if (hp
->nh_td
!= td
) continue;
604 hp
->nh_conn
->ref_cnt
-= hp
->nh_ref
;
605 SLIST_REMOVE(&lhlist
, hp
, ncp_handle
, nh_next
);
608 lockmgr(&lhlock
, LK_RELEASE
, 0);
612 * remove references in all possible connections,
613 * XXX - possible problem is a locked list.
616 ncp_conn_list_rm_ref(pid_t pid) {
617 struct ncp_conn *ncp;
619 ncp_conn_locklist(LK_SHARED, NULL);
620 SLIST_FOREACH(ncp, &conn_list, nc_next) {
621 ncp_conn_rm_ref(ncp,pid,1);
623 ncp_conn_unlocklist(NULL);
628 ncp_conn_getinfo(struct ncp_conn
*ncp
, struct ncp_conn_stat
*ncs
) {
629 bzero(ncs
,sizeof(*ncs
));
631 ncs
->li
.user
= ncs
->user
;
633 strcpy(ncs
->user
, ncp
->li
.user
);
634 ncs
->li
.password
= NULL
;
635 ncs
->connRef
= ncp
->nc_id
;
636 ncs
->ref_cnt
= ncp
->ref_cnt
;
637 ncs
->connid
= ncp
->connid
;
638 ncs
->owner
= ncp
->nc_owner
->cr_uid
;
639 ncs
->group
= ncp
->nc_group
;
640 ncs
->flags
= ncp
->flags
;
641 ncs
->buffer_size
= ncp
->buffer_size
;
646 ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS
)
649 struct ncp_conn_stat ncs
;
650 struct ncp_conn
*ncp
;
651 /* struct ucred *cred = req->td->td_ucred;*/
653 error
= sysctl_wire_old_buffer(req
, 0);
656 ncp_conn_locklist(LK_SHARED
, req
->td
);
657 error
= SYSCTL_OUT(req
, &ncp_conn_cnt
, sizeof(ncp_conn_cnt
));
658 SLIST_FOREACH(ncp
, &conn_list
, nc_next
) {
660 /* I can't do conn_lock while list is locked */
662 ncp_conn_getinfo(ncp
, &ncs
);
664 error
= SYSCTL_OUT(req
, &ncs
, sizeof(ncs
));
666 ncp_conn_unlocklist(req
->td
);