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
32 * $FreeBSD: src/sys/netncp/ncp_ncp.c,v 1.3 1999/10/29 10:21:07 bp Exp $
33 * $DragonFly: src/sys/netproto/ncp/ncp_ncp.c,v 1.15 2007/04/22 01:13:16 dillon Exp $
35 * Core of NCP protocol
40 #include <sys/param.h>
41 #include <sys/errno.h>
42 #include <sys/systm.h>
46 #include <sys/signalvar.h>
47 #include <sys/signal2.h>
49 #include <sys/thread2.h>
50 #include <sys/socketvar.h>
53 #include <netproto/ipx/ipx.h>
54 #include <netproto/ipx/ipx_var.h>
65 static int ncp_do_request(struct ncp_conn
*,struct ncp_rq
*rqp
);
66 static int ncp_negotiate_buffersize(struct ncp_conn
*conn
, int size
, int *target
);
67 static int ncp_renegotiate_connparam(struct ncp_conn
*conn
, int buffsize
, int in_options
);
68 static void ncp_sign_packet(struct ncp_conn
*conn
, struct ncp_rq
*rqp
, int *size
);
73 m_dumpm(struct mbuf
*m
)
83 kprintf("%02x ",((int)*(p
++)) & 0xff);
89 #endif /* NCP_DATA_DEBUG */
92 ncp_chkintr(struct ncp_conn
*conn
, struct thread
*td
)
95 struct lwp
*lp
= td
->td_lwp
;
96 struct proc
*p
= td
->td_proc
;
100 tmpset
= lwp_sigpend(lp
);
101 SIGSETNAND(tmpset
, lp
->lwp_sigmask
);
102 SIGSETNAND(tmpset
, p
->p_sigignore
);
103 if (SIGNOTEMPTY(tmpset
) && NCP_SIGMASK(tmpset
))
109 * Process initial NCP handshake (attach)
110 * NOTE: Since all functions below may change conn attributes, they
111 * should be called with LOCKED connection, also they use td & ucred
114 ncp_ncp_connect(struct ncp_conn
*conn
) {
116 struct ncp_rphdr
*rp
;
119 conn
->flags
&= ~(NCPFL_INVALID
| NCPFL_SIGNACTIVE
| NCPFL_SIGNWANTED
);
121 checkbad(ncp_rq_head(rqp
,NCP_ALLOC_SLOT
,0,conn
->td
,conn
->ucred
));
122 error
=ncp_do_request(conn
,rqp
);
124 rp
= mtod(rqp
->rp
, struct ncp_rphdr
*);
125 conn
->connid
= rp
->conn_low
+ (rp
->conn_high
<< 8);
128 if (error
) return error
;
129 conn
->flags
|= NCPFL_ATTACHED
;
131 error
= ncp_renegotiate_connparam(conn
, NCP_DEFAULT_BUFSIZE
, 0);
132 if (error
== NWE_SIGNATURE_LEVEL_CONFLICT
) {
133 kprintf("Unable to negotiate requested security level\n");
137 ncp_ncp_disconnect(conn
);
141 ncp_burst_connect(conn
);
148 ncp_ncp_disconnect(struct ncp_conn
*conn
) {
150 struct ncp_rqhdr
*ncprq
;
153 NCPSDEBUG("for connid=%d\n",conn
->nc_id
);
155 ncp_burst_disconnect(conn
);
157 error
=ncp_rq_head(rqp
,NCP_FREE_SLOT
,0,conn
->td
,conn
->ucred
);
158 ncprq
= mtod(rqp
->rq
,struct ncp_rqhdr
*);
159 error
=ncp_do_request(conn
,rqp
);
161 ncp_conn_invalidate(conn
);
162 ncp_sock_disconnect(conn
);
166 * Make a signature for the current packet and add it at the end of the
170 ncp_sign_packet(struct ncp_conn
*conn
, struct ncp_rq
*rqp
, int *size
) {
173 bzero(data
, sizeof(data
));
174 bcopy(conn
->sign_root
, data
, 8);
175 setdle(data
, 8, *size
);
176 m_copydata(rqp
->rq
, sizeof(struct ncp_rqhdr
)-1,
177 min((*size
) - sizeof(struct ncp_rqhdr
)+1, 52),data
+12);
178 ncp_sign(conn
->sign_state
, data
, conn
->sign_state
);
179 ncp_rq_mem(rqp
, (void*)conn
->sign_state
, 8);
184 * Low level send rpc, here we do not attempt to restore any connection,
185 * Connection expected to be locked
188 ncp_do_request(struct ncp_conn
*conn
, struct ncp_rq
*rqp
) {
189 int error
=EIO
,len
, dosend
, plen
= 0, gotpacket
;
191 struct thread
*td
= conn
->td
;
192 struct ncp_rqhdr
*rq
;
193 struct ncp_rphdr
*rp
=NULL
;
195 struct mbuf
*m
, *mreply
= NULL
;
200 td
= curthread
; /* XXX maybe procpage ? */
201 if (!ncp_conn_valid(conn
)) {
202 kprintf("%s: conn not valid\n",__func__
);
207 kprintf("%s: ncp_so is NULL !\n",__func__
);
208 ncp_conn_invalidate(conn
); /* wow ! how we do that ? */
212 * Flush out replies on previous reqs
215 while (1/*so->so_rcv.sb_cc*/) {
218 if (ncp_poll(so
, POLLIN
) == 0)
220 if (ncp_sock_recv(so
, &sio
) != 0)
224 rq
= mtod(rqp
->rq
,struct ncp_rqhdr
*);
232 rqp
->rq
->m_pkthdr
.len
= len
;
234 case 0x15: case 0x16: case 0x17: case 0x23:
236 *((u_int16_t
*)(mtod(m
,u_int8_t
*)+sizeof(*rq
))) = htons(len
-2-sizeof(*rq
));
239 if (conn
->flags
& NCPFL_SIGNACTIVE
) {
240 ncp_sign_packet(conn
, rqp
, &len
);
241 rqp
->rq
->m_pkthdr
.len
= len
;
243 rq
->conn_low
= conn
->connid
& 0xff;
244 /* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/
245 /* XXX: this is temporary fix till I find a better solution */
246 rq
->task
= rq
->conn_low
;
247 rq
->conn_high
= conn
->connid
>> 8;
248 rqp
->rexmit
= conn
->li
.retry_count
;
250 if (rqp
->rexmit
-- == 0) {
256 NCPSDEBUG("send:%04x f=%02x c=%d l=%d s=%d t=%d\n",rq
->type
, rq
->fn
, (rq
->conn_high
<< 8) + rq
->conn_low
,
257 rqp
->rq
->m_pkthdr
.len
, rq
->seq
, rq
->task
259 error
= ncp_sock_send(so
, rqp
->rq
, rqp
);
262 tv
.tv_sec
= conn
->li
.timeout
;
264 error
= ncp_sock_rselect(so
, td
, &tv
, POLLIN
);
265 if (error
== EWOULDBLOCK
) /* timeout expired */
267 error
= ncp_chkintr(conn
, td
);
268 if (error
== EINTR
) /* we dont restart */
272 * At this point it is possible to get more than one
273 * reply from server. In general, last reply should be for
274 * current request, but not always. So, we loop through
275 * all replies to find the right answer and flush others.
277 gotpacket
= 0; /* nothing good found */
278 dosend
= 1; /* resend rq if error */
283 if (ncp_poll(so
,POLLIN
) == 0) break;
284 error
= ncp_sock_recv(so
, &sio
);
285 if (error
) break; /* must be more checks !!! */
288 if (m
->m_len
< sizeof(*rp
)) {
289 m
= m_pullup(m
, sizeof(*rp
));
291 kprintf("%s: reply too short\n",__func__
);
295 rp
= mtod(m
, struct ncp_rphdr
*);
296 if (sio
.sb_cc
== sizeof(*rp
) && rp
->type
== NCP_POSITIVE_ACK
) {
297 NCPSDEBUG("got positive acknowledge\n");
299 rqp
->rexmit
= conn
->li
.retry_count
;
300 dosend
= 0; /* server just busy and will reply ASAP */
303 NCPSDEBUG("recv:%04x c=%d l=%d s=%d t=%d cc=%02x cs=%02x\n",rp
->type
,
304 (rp
->conn_high
<< 8) + rp
->conn_low
, sio
.len
, rp
->seq
, rp
->task
,
305 rp
->completion_code
, rp
->connection_state
);
307 if ( (rp
->type
== NCP_REPLY
) &&
308 ((rq
->type
== NCP_ALLOC_SLOT
) ||
309 ((rp
->conn_low
== rq
->conn_low
) &&
310 (rp
->conn_high
== rq
->conn_high
)
312 if (rq
->seq
> rp
->seq
|| (rq
->seq
== 0 && rp
->seq
== 0xff)) {
315 if (rp
->seq
== rq
->seq
) {
323 continue; /* look up other for other packets */
327 NCPSDEBUG("reply mismatch\n");
330 if (gotpacket
) break;
331 /* try to resend, or just wait */
336 NCPSDEBUG("error=%d\n",error
);
337 if (error
!= EINTR
) /* if not just interrupt */
338 ncp_conn_invalidate(conn
); /* only reconnect to restore */
341 if (conn
->flags
& NCPFL_SIGNACTIVE
) {
342 /* XXX: check reply signature */
348 rp
= mtod(m
, struct ncp_rphdr
*);
351 rqp
->cc
= error
= rp
->completion_code
;
352 if (error
) error
|= 0x8900; /* server error */
353 rqp
->cs
= rp
->connection_state
;
354 if (rqp
->cs
& (NCP_CS_BAD_CONN
| NCP_CS_SERVER_DOWN
)) {
355 NCPSDEBUG("server drop us\n");
356 ncp_conn_invalidate(conn
);
361 rqp
->bpos
= mtod(m
, caddr_t
) + sizeof(*rp
);
366 * Here we will try to restore any loggedin & dropped connection,
367 * connection should be locked on entry
369 int ncp_restore_login(struct ncp_conn
*conn
);
371 ncp_restore_login(struct ncp_conn
*conn
) {
374 if (conn
->flags
& NCPFL_RESTORING
) {
375 kprintf("Hey, ncp_restore_login called twise !!!\n");
378 oldflags
= conn
->flags
;
379 kprintf("Restoring connection, flags = %d\n",oldflags
);
380 if ((oldflags
& NCPFL_LOGGED
) == 0) {
381 return ECONNRESET
; /* no need to restore empty conn */
383 conn
->flags
&= ~(NCPFL_LOGGED
| NCPFL_ATTACHED
);
384 conn
->flags
|= NCPFL_RESTORING
;
385 do { /* not a loop */
386 error
= ncp_reconnect(conn
);
389 error
= ncp_login_object(conn
, conn
->li
.user
, conn
->li
.objtype
, conn
->li
.password
,conn
->td
,conn
->ucred
);
391 conn
->flags
|= NCPFL_LOGGED
;
394 conn
->flags
= oldflags
| NCPFL_INVALID
;
396 conn
->flags
&= ~NCPFL_RESTORING
;
401 ncp_request(struct ncp_conn
*conn
, struct ncp_rq
*rqp
) {
403 /* struct ncp_rqhdr *rq = mtod(rqp->rq,struct ncp_rqhdr*);*/
405 error
= ncp_conn_lock(conn
,rqp
->td
,rqp
->cred
,NCPM_EXECUTE
);
406 if (error
) return error
;
407 rcnt
= NCP_RESTORE_COUNT
;
409 if (!ncp_conn_valid(conn
)) {
415 error
= ncp_restore_login(conn
);
419 error
=ncp_do_request(conn
, rqp
);
420 if (ncp_conn_valid(conn
)) /* not just error ! */
423 ncp_conn_unlock(conn
,rqp
->td
);
428 * All negotiation functions expect a locked connection
431 ncp_negotiate_buffersize(struct ncp_conn
*conn
, int size
, int *target
) {
435 NCP_RQ_HEAD(0x21,conn
->td
,conn
->ucred
);
436 ncp_rq_word_hl(rqp
, size
);
437 checkbad(ncp_request(conn
,rqp
));
438 *target
= min(ncp_rp_word_hl(rqp
), size
);
444 ncp_negotiate_size_and_options(struct ncp_conn
*conn
, int size
, int options
,
445 int *ret_size
, int *ret_options
) {
450 NCP_RQ_HEAD(0x61,conn
->td
,conn
->ucred
);
451 ncp_rq_word_hl(rqp
, size
);
452 ncp_rq_byte(rqp
, options
);
453 checkbad(ncp_request(conn
, rqp
));
454 rs
= ncp_rp_word_hl(rqp
);
455 *ret_size
= (rs
== 0) ? size
: min(rs
, size
);
456 ncp_rp_word_hl(rqp
); /* skip echo socket */
457 *ret_options
= ncp_rp_byte(rqp
);
463 ncp_renegotiate_connparam(struct ncp_conn
*conn
, int buffsize
, int in_options
)
465 int neg_buffsize
, error
, options
, sl
;
467 sl
= conn
->li
.sig_level
;
469 in_options
|= NCP_SECURITY_LEVEL_SIGN_HEADERS
;
472 in_options
|= NCP_IPX_CHECKSUM
;
474 error
= ncp_negotiate_size_and_options(conn
, buffsize
, in_options
,
475 &neg_buffsize
, &options
);
478 if ((options
^ in_options
) & NCP_IPX_CHECKSUM
) {
480 kprintf("Server refuses to support IPX checksums\n");
481 return NWE_REQUESTER_FAILURE
;
483 in_options
|= NCP_IPX_CHECKSUM
;
487 if ((options
^ in_options
) & 2) {
488 if (sl
== 0 || sl
== 3)
489 return NWE_SIGNATURE_LEVEL_CONFLICT
;
491 in_options
|= NCP_SECURITY_LEVEL_SIGN_HEADERS
;
496 error
= ncp_negotiate_size_and_options(conn
,
497 buffsize
, in_options
, &neg_buffsize
, &options
);
498 if ((options
^ in_options
) & 3) {
499 return NWE_SIGNATURE_LEVEL_CONFLICT
;
503 in_options
&= ~NCP_SECURITY_LEVEL_SIGN_HEADERS
;
504 error
= ncp_negotiate_buffersize(conn
, NCP_DEFAULT_BUFSIZE
,
507 if (error
) return error
;
508 if ((neg_buffsize
< 512) || (neg_buffsize
> NCP_MAX_BUFSIZE
))
510 conn
->buffer_size
= neg_buffsize
;
511 if (in_options
& NCP_SECURITY_LEVEL_SIGN_HEADERS
)
512 conn
->flags
|= NCPFL_SIGNWANTED
;
514 ncp_sock_checksum(conn
, in_options
& NCP_IPX_CHECKSUM
);
520 ncp_reconnect(struct ncp_conn
*conn
) {
523 /* close any open sockets */
524 ncp_sock_disconnect(conn
);
525 switch( conn
->li
.saddr
.sa_family
) {
528 error
= ncp_sock_connect_ipx(conn
);
533 error
= ncp_sock_connect_in(conn
);
537 return EPROTONOSUPPORT
;
540 error
= ncp_ncp_connect(conn
);
545 * Create conn structure and try to do low level connect
546 * Server addr should be filled in.
549 ncp_connect(struct ncp_conn_args
*li
, struct thread
*td
, struct ucred
*cred
,
550 struct ncp_conn
**aconn
)
552 struct ncp_conn
*conn
;
556 if (li
->saddr
.sa_family
!= AF_INET
&& li
->saddr
.sa_family
!= AF_IPX
)
557 return EPROTONOSUPPORT
;
558 isroot
= ncp_suser(cred
) == 0;
560 * Only root can change ownership
562 if (li
->owner
!= NCP_DEFAULT_OWNER
&& !isroot
)
564 if (li
->group
!= NCP_DEFAULT_GROUP
&&
565 !groupmember(li
->group
, cred
) && !isroot
)
567 if (li
->owner
!= NCP_DEFAULT_OWNER
) {
569 owner
->cr_uid
= li
->owner
;
571 owner
= crhold(cred
);
573 error
= ncp_conn_alloc(td
, owner
, &conn
);
581 conn
->nc_group
= (li
->group
!= NCP_DEFAULT_GROUP
) ?
582 li
->group
: cred
->cr_groups
[0];
584 if (li
->retry_count
== 0)
585 conn
->li
.retry_count
= NCP_RETRY_COUNT
;
586 if (li
->timeout
== 0)
587 conn
->li
.timeout
= NCP_RETRY_TIMEOUT
;
588 error
= ncp_reconnect(conn
);
590 ncp_disconnect(conn
);
597 * Break connection and deallocate memory
600 ncp_disconnect(struct ncp_conn
*conn
) {
602 if (ncp_conn_access(conn
,conn
->ucred
,NCPM_WRITE
))
604 if (conn
->ref_cnt
!= 0) return EBUSY
;
605 if (conn
->flags
& NCPFL_PERMANENT
) return EBUSY
;
606 if (ncp_conn_valid(conn
)) {
607 ncp_ncp_disconnect(conn
);
609 ncp_sock_disconnect(conn
);
615 ncp_check_rq(struct ncp_conn
*conn
){
617 if (conn
->flags
& NCPFL_INTR
) return;
618 /* first, check for signals */
619 if (ncp_chkintr(conn
,conn
->td
)) {
620 conn
->flags
|= NCPFL_INTR
;