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.2 2003/06/17 04:28:53 dillon Exp $
35 * Core of NCP protocol
40 #include <sys/param.h>
41 #include <sys/errno.h>
42 #include <sys/systm.h>
45 #include <sys/signalvar.h>
49 #include <netipx/ipx.h>
50 #include <netipx/ipx_var.h>
53 #include <netncp/ncp.h>
54 #include <netncp/ncp_conn.h>
55 #include <netncp/ncp_sock.h>
56 #include <netncp/ncp_subr.h>
57 #include <netncp/ncp_ncp.h>
58 #include <netncp/ncp_rq.h>
59 #include <netncp/nwerror.h>
61 static int ncp_do_request(struct ncp_conn *,struct ncp_rq *rqp);
62 static int ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target);
63 static int ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options);
64 static void ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size);
69 void m_dumpm(struct mbuf *m) {
78 printf("%02x ",((int)*(p++)) & 0xff);
84 #endif /* NCP_DATA_DEBUG */
87 ncp_chkintr(struct ncp_conn *conn, struct proc *p)
93 tmpset = p->p_siglist;
94 SIGSETNAND(tmpset, p->p_sigmask);
95 SIGSETNAND(tmpset, p->p_sigignore);
96 if (SIGNOTEMPTY(p->p_siglist) && NCP_SIGMASK(tmpset))
102 * Process initial NCP handshake (attach)
103 * NOTE: Since all functions below may change conn attributes, they
104 * should be called with LOCKED connection, also they use procp & ucred
107 ncp_ncp_connect(struct ncp_conn *conn) {
109 struct ncp_rphdr *rp;
112 conn->flags &= ~(NCPFL_INVALID | NCPFL_SIGNACTIVE | NCPFL_SIGNWANTED);
114 checkbad(ncp_rq_head(rqp,NCP_ALLOC_SLOT,0,conn->procp,conn->ucred));
115 error=ncp_do_request(conn,rqp);
117 rp = mtod(rqp->rp, struct ncp_rphdr*);
118 conn->connid = rp->conn_low + (rp->conn_high << 8);
121 if (error) return error;
122 conn->flags |= NCPFL_ATTACHED;
124 error = ncp_renegotiate_connparam(conn, NCP_DEFAULT_BUFSIZE, 0);
125 if (error == NWE_SIGNATURE_LEVEL_CONFLICT) {
126 printf("Unable to negotiate requested security level\n");
130 ncp_ncp_disconnect(conn);
134 ncp_burst_connect(conn);
141 ncp_ncp_disconnect(struct ncp_conn *conn) {
143 struct ncp_rqhdr *ncprq;
146 NCPSDEBUG("for connid=%d\n",conn->nc_id);
148 ncp_burst_disconnect(conn);
150 error=ncp_rq_head(rqp,NCP_FREE_SLOT,0,conn->procp,conn->ucred);
151 ncprq = mtod(rqp->rq,struct ncp_rqhdr*);
152 error=ncp_do_request(conn,rqp);
154 ncp_conn_invalidate(conn);
155 ncp_sock_disconnect(conn);
159 * Make a signature for the current packet and add it at the end of the
163 ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size) {
166 bzero(data, sizeof(data));
167 bcopy(conn->sign_root, data, 8);
168 setdle(data, 8, *size);
169 m_copydata(rqp->rq, sizeof(struct ncp_rqhdr)-1,
170 min((*size) - sizeof(struct ncp_rqhdr)+1, 52),data+12);
171 ncp_sign(conn->sign_state, data, conn->sign_state);
172 ncp_rq_mem(rqp, (void*)conn->sign_state, 8);
177 * Low level send rpc, here we do not attempt to restore any connection,
178 * Connection expected to be locked
181 ncp_do_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
182 int error=EIO,len, dosend, plen = 0, gotpacket, s;
184 struct proc *p = conn->procp;
185 struct ncp_rqhdr *rq;
186 struct ncp_rphdr *rp=NULL;
188 struct mbuf *m, *mreply = NULL;
193 p = curproc; /* XXX maybe procpage ? */
194 if (!ncp_conn_valid(conn)) {
195 printf("%s: conn not valid\n",__FUNCTION__);
200 printf("%s: ncp_so is NULL !\n",__FUNCTION__);
201 ncp_conn_invalidate(conn); /* wow ! how we do that ? */
205 * Flush out replies on previous reqs
208 while (1/*so->so_rcv.sb_cc*/) {
209 if (ncp_poll(so,POLLIN) == 0) break;
210 if (ncp_sock_recv(so,&m,&len) != 0) break;
213 rq = mtod(rqp->rq,struct ncp_rqhdr *);
221 rqp->rq->m_pkthdr.len = len;
223 case 0x15: case 0x16: case 0x17: case 0x23:
225 *((u_int16_t*)(mtod(m,u_int8_t*)+sizeof(*rq))) = htons(len-2-sizeof(*rq));
228 if (conn->flags & NCPFL_SIGNACTIVE) {
229 ncp_sign_packet(conn, rqp, &len);
230 rqp->rq->m_pkthdr.len = len;
232 rq->conn_low = conn->connid & 0xff;
233 /* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/
234 /* XXX: this is temporary fix till I find a better solution */
235 rq->task = rq->conn_low;
236 rq->conn_high = conn->connid >> 8;
237 rqp->rexmit = conn->li.retry_count;
239 if (rqp->rexmit-- == 0) {
245 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,
246 rqp->rq->m_pkthdr.len, rq->seq, rq->task
248 error = ncp_sock_send(so, rqp->rq, rqp);
251 tv.tv_sec = conn->li.timeout;
253 error = ncp_sock_rselect(so, p, &tv, POLLIN);
254 if (error == EWOULDBLOCK ) /* timeout expired */
256 error = ncp_chkintr(conn, p);
257 if (error == EINTR) /* we dont restart */
261 * At this point it is possible to get more than one
262 * reply from server. In general, last reply should be for
263 * current request, but not always. So, we loop through
264 * all replies to find the right answer and flush others.
266 gotpacket = 0; /* nothing good found */
267 dosend = 1; /* resend rq if error */
270 if (ncp_poll(so,POLLIN) == 0) break;
271 /* if (so->so_rcv.sb_cc == 0) {
274 error = ncp_sock_recv(so,&m,&len);
275 if (error) break; /* must be more checks !!! */
276 if (m->m_len < sizeof(*rp)) {
277 m = m_pullup(m, sizeof(*rp));
279 printf("%s: reply too short\n",__FUNCTION__);
283 rp = mtod(m, struct ncp_rphdr*);
284 if (len == sizeof(*rp) && rp->type == NCP_POSITIVE_ACK) {
285 NCPSDEBUG("got positive acknowledge\n");
287 rqp->rexmit = conn->li.retry_count;
288 dosend = 0; /* server just busy and will reply ASAP */
291 NCPSDEBUG("recv:%04x c=%d l=%d s=%d t=%d cc=%02x cs=%02x\n",rp->type,
292 (rp->conn_high << 8) + rp->conn_low, len, rp->seq, rp->task,
293 rp->completion_code, rp->connection_state);
295 if ( (rp->type == NCP_REPLY) &&
296 ((rq->type == NCP_ALLOC_SLOT) ||
297 ((rp->conn_low == rq->conn_low) &&
298 (rp->conn_high == rq->conn_high)
300 if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) {
303 if (rp->seq == rq->seq) {
311 continue; /* look up other for other packets */
315 NCPSDEBUG("reply mismatch\n");
318 if (gotpacket) break;
319 /* try to resend, or just wait */
324 NCPSDEBUG("error=%d\n",error);
325 if (error != EINTR) /* if not just interrupt */
326 ncp_conn_invalidate(conn); /* only reconnect to restore */
329 if (conn->flags & NCPFL_SIGNACTIVE) {
330 /* XXX: check reply signature */
336 rp = mtod(m, struct ncp_rphdr*);
339 rqp->cc = error = rp->completion_code;
340 if (error) error |= 0x8900; /* server error */
341 rqp->cs = rp->connection_state;
342 if (rqp->cs & (NCP_CS_BAD_CONN | NCP_CS_SERVER_DOWN)) {
343 NCPSDEBUG("server drop us\n");
344 ncp_conn_invalidate(conn);
349 rqp->bpos = mtod(m, caddr_t) + sizeof(*rp);
354 * Here we will try to restore any loggedin & dropped connection,
355 * connection should be locked on entry
357 int ncp_restore_login(struct ncp_conn *conn);
359 ncp_restore_login(struct ncp_conn *conn) {
362 if (conn->flags & NCPFL_RESTORING) {
363 printf("Hey, ncp_restore_login called twise !!!\n");
366 oldflags = conn->flags;
367 printf("Restoring connection, flags = %d\n",oldflags);
368 if ((oldflags & NCPFL_LOGGED) == 0) {
369 return ECONNRESET; /* no need to restore empty conn */
371 conn->flags &= ~(NCPFL_LOGGED | NCPFL_ATTACHED);
372 conn->flags |= NCPFL_RESTORING;
373 do { /* not a loop */
374 error = ncp_reconnect(conn);
377 error = ncp_login_object(conn, conn->li.user, conn->li.objtype, conn->li.password,conn->procp,conn->ucred);
379 conn->flags |= NCPFL_LOGGED;
382 conn->flags = oldflags | NCPFL_INVALID;
384 conn->flags &= ~NCPFL_RESTORING;
389 ncp_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
391 /* struct ncp_rqhdr *rq = mtod(rqp->rq,struct ncp_rqhdr*);*/
393 error = ncp_conn_lock(conn,rqp->p,rqp->cred,NCPM_EXECUTE);
394 if (error) return error;
395 rcnt = NCP_RESTORE_COUNT;
397 if (!ncp_conn_valid(conn)) {
403 error = ncp_restore_login(conn);
407 error=ncp_do_request(conn, rqp);
408 if (ncp_conn_valid(conn)) /* not just error ! */
411 ncp_conn_unlock(conn,rqp->p);
416 * All negotiation functions expect a locked connection
419 ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target) {
423 NCP_RQ_HEAD(0x21,conn->procp,conn->ucred);
424 ncp_rq_word_hl(rqp, size);
425 checkbad(ncp_request(conn,rqp));
426 *target = min(ncp_rp_word_hl(rqp), size);
432 ncp_negotiate_size_and_options(struct ncp_conn *conn, int size, int options,
433 int *ret_size, int *ret_options) {
438 NCP_RQ_HEAD(0x61,conn->procp,conn->ucred);
439 ncp_rq_word_hl(rqp, size);
440 ncp_rq_byte(rqp, options);
441 checkbad(ncp_request(conn, rqp));
442 rs = ncp_rp_word_hl(rqp);
443 *ret_size = (rs == 0) ? size : min(rs, size);
444 ncp_rp_word_hl(rqp); /* skip echo socket */
445 *ret_options = ncp_rp_byte(rqp);
451 ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options)
453 int neg_buffsize, error, options, sl;
455 sl = conn->li.sig_level;
457 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
460 in_options |= NCP_IPX_CHECKSUM;
462 error = ncp_negotiate_size_and_options(conn, buffsize, in_options,
463 &neg_buffsize, &options);
466 if ((options ^ in_options) & NCP_IPX_CHECKSUM) {
468 printf("Server refuses to support IPX checksums\n");
469 return NWE_REQUESTER_FAILURE;
471 in_options |= NCP_IPX_CHECKSUM;
475 if ((options ^ in_options) & 2) {
476 if (sl == 0 || sl == 3)
477 return NWE_SIGNATURE_LEVEL_CONFLICT;
479 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
484 error = ncp_negotiate_size_and_options(conn,
485 buffsize, in_options, &neg_buffsize, &options);
486 if ((options ^ in_options) & 3) {
487 return NWE_SIGNATURE_LEVEL_CONFLICT;
491 in_options &= ~NCP_SECURITY_LEVEL_SIGN_HEADERS;
492 error = ncp_negotiate_buffersize(conn, NCP_DEFAULT_BUFSIZE,
495 if (error) return error;
496 if ((neg_buffsize < 512) || (neg_buffsize > NCP_MAX_BUFSIZE))
498 conn->buffer_size = neg_buffsize;
499 if (in_options & NCP_SECURITY_LEVEL_SIGN_HEADERS)
500 conn->flags |= NCPFL_SIGNWANTED;
502 ncp_sock_checksum(conn, in_options & NCP_IPX_CHECKSUM);
508 ncp_reconnect(struct ncp_conn *conn) {
511 /* close any open sockets */
512 ncp_sock_disconnect(conn);
513 switch( conn->li.saddr.sa_family ) {
516 error = ncp_sock_connect_ipx(conn);
521 error = ncp_sock_connect_in(conn);
525 return EPROTONOSUPPORT;
528 error = ncp_ncp_connect(conn);
533 * Create conn structure and try to do low level connect
534 * Server addr should be filled in.
537 ncp_connect(struct ncp_conn_args *li, struct proc *p, struct ucred *cred,
538 struct ncp_conn **aconn)
540 struct ncp_conn *conn;
544 if (li->saddr.sa_family != AF_INET && li->saddr.sa_family != AF_IPX)
545 return EPROTONOSUPPORT;
546 isroot = ncp_suser(cred) == 0;
548 * Only root can change ownership
550 if (li->owner != NCP_DEFAULT_OWNER && !isroot)
552 if (li->group != NCP_DEFAULT_GROUP &&
553 !groupmember(li->group, cred) && !isroot)
555 if (li->owner != NCP_DEFAULT_OWNER) {
557 owner->cr_uid = li->owner;
562 error = ncp_conn_alloc(p, owner, &conn);
570 conn->nc_group = (li->group != NCP_DEFAULT_GROUP) ?
571 li->group : cred->cr_groups[0];
573 if (li->retry_count == 0)
574 conn->li.retry_count = NCP_RETRY_COUNT;
575 if (li->timeout == 0)
576 conn->li.timeout = NCP_RETRY_TIMEOUT;
577 error = ncp_reconnect(conn);
579 ncp_disconnect(conn);
586 * Break connection and deallocate memory
589 ncp_disconnect(struct ncp_conn *conn) {
591 if (ncp_conn_access(conn,conn->ucred,NCPM_WRITE))
593 if (conn->ref_cnt != 0) return EBUSY;
594 if (conn->flags & NCPFL_PERMANENT) return EBUSY;
595 if (ncp_conn_valid(conn)) {
596 ncp_ncp_disconnect(conn);
598 ncp_sock_disconnect(conn);
604 ncp_check_rq(struct ncp_conn *conn){
606 if (conn->flags & NCPFL_INTR) return;
607 /* first, check for signals */
608 if (ncp_chkintr(conn,conn->procp)) {
609 conn->flags |= NCPFL_INTR;