amd64: declare initializecpu outside of SMP
[dragonfly.git] / sys / netproto / ncp / ncp_ncp.c
blobf39fc8d010df8bbad273ade7dce9fcd27a188bf2
1 /*
2 * Copyright (c) 1999, Boris Popov
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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
30 * SUCH DAMAGE.
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
37 #include "opt_inet.h"
38 #include "opt_ipx.h"
40 #include <sys/param.h>
41 #include <sys/errno.h>
42 #include <sys/systm.h>
43 #include <sys/proc.h>
44 #include <sys/priv.h>
45 #include <sys/poll.h>
46 #include <sys/signalvar.h>
47 #include <sys/signal2.h>
48 #include <sys/mbuf.h>
49 #include <sys/thread2.h>
50 #include <sys/socketvar.h>
52 #ifdef IPX
53 #include <netproto/ipx/ipx.h>
54 #include <netproto/ipx/ipx_var.h>
55 #endif
57 #include "ncp.h"
58 #include "ncp_conn.h"
59 #include "ncp_sock.h"
60 #include "ncp_subr.h"
61 #include "ncp_ncp.h"
62 #include "ncp_rq.h"
63 #include "nwerror.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);
71 #ifdef NCP_DATA_DEBUG
72 static void
73 m_dumpm(struct mbuf *m)
75 char *p;
76 int len;
77 kprintf("d=");
78 while(m) {
79 p=mtod(m,char *);
80 len=m->m_len;
81 kprintf("(%d)",len);
82 while(len--){
83 kprintf("%02x ",((int)*(p++)) & 0xff);
85 m=m->m_next;
87 kprintf("\n");
89 #endif /* NCP_DATA_DEBUG */
91 int
92 ncp_chkintr(struct ncp_conn *conn, struct thread *td)
94 sigset_t tmpset;
95 struct lwp *lp = td->td_lwp;
96 struct proc *p = td->td_proc;
98 if (p == NULL)
99 return 0;
100 tmpset = lwp_sigpend(lp);
101 SIGSETNAND(tmpset, lp->lwp_sigmask);
102 SIGSETNAND(tmpset, p->p_sigignore);
103 if (SIGNOTEMPTY(tmpset) && NCP_SIGMASK(tmpset))
104 return EINTR;
105 return 0;
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) {
115 int error;
116 struct ncp_rphdr *rp;
117 DECLARE_RQ;
119 conn->flags &= ~(NCPFL_INVALID | NCPFL_SIGNACTIVE | NCPFL_SIGNWANTED);
120 conn->seq = 0;
121 checkbad(ncp_rq_head(rqp,NCP_ALLOC_SLOT,0,conn->td,conn->ucred));
122 error=ncp_do_request(conn,rqp);
123 if (!error) {
124 rp = mtod(rqp->rp, struct ncp_rphdr*);
125 conn->connid = rp->conn_low + (rp->conn_high << 8);
127 ncp_rq_done(rqp);
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");
134 error = EOPNOTSUPP;
136 if (error) {
137 ncp_ncp_disconnect(conn);
138 return error;
140 #ifdef NCPBURST
141 ncp_burst_connect(conn);
142 #endif
143 bad:
144 return error;
148 ncp_ncp_disconnect(struct ncp_conn *conn) {
149 int error;
150 struct ncp_rqhdr *ncprq;
151 DECLARE_RQ;
153 NCPSDEBUG("for connid=%d\n",conn->nc_id);
154 #ifdef NCPBURST
155 ncp_burst_disconnect(conn);
156 #endif
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);
160 ncp_rq_done(rqp);
161 ncp_conn_invalidate(conn);
162 ncp_sock_disconnect(conn);
163 return 0;
166 * Make a signature for the current packet and add it at the end of the
167 * packet.
169 static void
170 ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size) {
171 u_char data[64];
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);
180 (*size) += 8;
184 * Low level send rpc, here we do not attempt to restore any connection,
185 * Connection expected to be locked
187 static int
188 ncp_do_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
189 int error=EIO,len, dosend, plen = 0, gotpacket;
190 struct socket *so;
191 struct thread *td = conn->td;
192 struct ncp_rqhdr *rq;
193 struct ncp_rphdr *rp=NULL;
194 struct timeval tv;
195 struct mbuf *m, *mreply = NULL;
197 conn->nc_rq = rqp;
198 rqp->conn = conn;
199 if (td == NULL)
200 td = curthread; /* XXX maybe procpage ? */
201 if (!ncp_conn_valid(conn)) {
202 kprintf("%s: conn not valid\n",__func__);
203 return (error);
205 so = conn->ncp_so;
206 if (!so) {
207 kprintf("%s: ncp_so is NULL !\n",__func__);
208 ncp_conn_invalidate(conn); /* wow ! how we do that ? */
209 return EBADF;
212 * Flush out replies on previous reqs
214 crit_enter();
215 while (1/*so->so_rcv.sb_cc*/) {
216 struct sockbuf sio;
218 if (ncp_poll(so, POLLIN) == 0)
219 break;
220 if (ncp_sock_recv(so, &sio) != 0)
221 break;
222 sbflush(&sio);
224 rq = mtod(rqp->rq,struct ncp_rqhdr *);
225 rq->seq = conn->seq;
226 m = rqp->rq;
227 len = 0;
228 while (m) {
229 len += m->m_len;
230 m = m->m_next;
232 rqp->rq->m_pkthdr.len = len;
233 switch(rq->fn) {
234 case 0x15: case 0x16: case 0x17: case 0x23:
235 m = rqp->rq;
236 *((u_int16_t*)(mtod(m,u_int8_t*)+sizeof(*rq))) = htons(len-2-sizeof(*rq));
237 break;
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;
249 for(dosend = 1;;) {
250 if (rqp->rexmit-- == 0) {
251 error = ETIMEDOUT;
252 break;
254 error = 0;
255 if (dosend) {
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);
260 if (error) break;
262 tv.tv_sec = conn->li.timeout;
263 tv.tv_usec = 0;
264 error = ncp_sock_rselect(so, td, &tv, POLLIN);
265 if (error == EWOULDBLOCK ) /* timeout expired */
266 continue;
267 error = ncp_chkintr(conn, td);
268 if (error == EINTR) /* we dont restart */
269 break;
270 if (error) break;
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 */
279 for (;;) {
280 struct sockbuf sio;
282 error = 0;
283 if (ncp_poll(so,POLLIN) == 0) break;
284 error = ncp_sock_recv(so, &sio);
285 if (error) break; /* must be more checks !!! */
287 m = sio.sb_mb;
288 if (m->m_len < sizeof(*rp)) {
289 m = m_pullup(m, sizeof(*rp));
290 if (m == NULL) {
291 kprintf("%s: reply too short\n",__func__);
292 continue;
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");
298 m_freem(m);
299 rqp->rexmit = conn->li.retry_count;
300 dosend = 0; /* server just busy and will reply ASAP */
301 continue;
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);
306 NCPDDEBUG(m);
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)
311 ))) {
312 if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) {
313 dosend = 1;
315 if (rp->seq == rq->seq) {
316 if (gotpacket) {
317 m_freem(m);
318 } else {
319 gotpacket = 1;
320 mreply = m;
321 plen = sio.sb_cc;
323 continue; /* look up other for other packets */
326 m_freem(m);
327 NCPSDEBUG("reply mismatch\n");
328 } /* for receive */
329 if (error) break;
330 if (gotpacket) break;
331 /* try to resend, or just wait */
333 crit_exit();
334 conn->seq++;
335 if (error) {
336 NCPSDEBUG("error=%d\n",error);
337 if (error != EINTR) /* if not just interrupt */
338 ncp_conn_invalidate(conn); /* only reconnect to restore */
339 return(error);
341 if (conn->flags & NCPFL_SIGNACTIVE) {
342 /* XXX: check reply signature */
343 m_adj(mreply, -8);
344 plen -= 8;
346 len = plen;
347 m = mreply;
348 rp = mtod(m, struct ncp_rphdr*);
349 len -= sizeof(*rp);
350 rqp->rpsize = len;
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);
357 error = ECONNRESET;
359 rqp->rp = m;
360 rqp->mrp = m;
361 rqp->bpos = mtod(m, caddr_t) + sizeof(*rp);
362 return error;
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) {
372 int error, oldflags;
374 if (conn->flags & NCPFL_RESTORING) {
375 kprintf("Hey, ncp_restore_login called twise !!!\n");
376 return 0;
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);
387 if (error) break;
388 if (conn->li.user)
389 error = ncp_login_object(conn, conn->li.user, conn->li.objtype, conn->li.password,conn->td,conn->ucred);
390 if (error) break;
391 conn->flags |= NCPFL_LOGGED;
392 } while(0);
393 if (error) {
394 conn->flags = oldflags | NCPFL_INVALID;
396 conn->flags &= ~NCPFL_RESTORING;
397 return error;
401 ncp_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
402 int error, rcnt;
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;
408 for(;;) {
409 if (!ncp_conn_valid(conn)) {
410 if (rcnt==0) {
411 error = ECONNRESET;
412 break;
414 rcnt--;
415 error = ncp_restore_login(conn);
416 if (error)
417 continue;
419 error=ncp_do_request(conn, rqp);
420 if (ncp_conn_valid(conn)) /* not just error ! */
421 break;
423 ncp_conn_unlock(conn,rqp->td);
424 return error;
428 * All negotiation functions expect a locked connection
430 static int
431 ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target) {
432 int error;
433 DECLARE_RQ;
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);
439 NCP_RQ_EXIT;
440 return error;
443 static int
444 ncp_negotiate_size_and_options(struct ncp_conn *conn, int size, int options,
445 int *ret_size, int *ret_options) {
446 int error;
447 int rs;
448 DECLARE_RQ;
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);
458 NCP_RQ_EXIT;
459 return error;
462 static int
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;
468 if (sl >= 2)
469 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
470 #ifdef IPX
471 if (ipxcksum == 2)
472 in_options |= NCP_IPX_CHECKSUM;
473 #endif
474 error = ncp_negotiate_size_and_options(conn, buffsize, in_options,
475 &neg_buffsize, &options);
476 if (!error) {
477 #ifdef IPX
478 if ((options ^ in_options) & NCP_IPX_CHECKSUM) {
479 if (ipxcksum == 2) {
480 kprintf("Server refuses to support IPX checksums\n");
481 return NWE_REQUESTER_FAILURE;
483 in_options |= NCP_IPX_CHECKSUM;
484 error = 1;
486 #endif /* IPX */
487 if ((options ^ in_options) & 2) {
488 if (sl == 0 || sl == 3)
489 return NWE_SIGNATURE_LEVEL_CONFLICT;
490 if (sl == 1) {
491 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
492 error = 1;
495 if (error) {
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;
502 } else {
503 in_options &= ~NCP_SECURITY_LEVEL_SIGN_HEADERS;
504 error = ncp_negotiate_buffersize(conn, NCP_DEFAULT_BUFSIZE,
505 &neg_buffsize);
507 if (error) return error;
508 if ((neg_buffsize < 512) || (neg_buffsize > NCP_MAX_BUFSIZE))
509 return EINVAL;
510 conn->buffer_size = neg_buffsize;
511 if (in_options & NCP_SECURITY_LEVEL_SIGN_HEADERS)
512 conn->flags |= NCPFL_SIGNWANTED;
513 #ifdef IPX
514 ncp_sock_checksum(conn, in_options & NCP_IPX_CHECKSUM);
515 #endif
516 return 0;
520 ncp_reconnect(struct ncp_conn *conn) {
521 int error;
523 /* close any open sockets */
524 ncp_sock_disconnect(conn);
525 switch( conn->li.saddr.sa_family ) {
526 #ifdef IPX
527 case AF_IPX:
528 error = ncp_sock_connect_ipx(conn);
529 break;
530 #endif
531 #ifdef INET
532 case AF_INET:
533 error = ncp_sock_connect_in(conn);
534 break;
535 #endif
536 default:
537 return EPROTONOSUPPORT;
539 if (!error)
540 error = ncp_ncp_connect(conn);
541 return error;
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;
553 struct ucred *owner;
554 int error, isroot;
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)
563 return EPERM;
564 if (li->group != NCP_DEFAULT_GROUP &&
565 !groupmember(li->group, cred) && !isroot)
566 return EPERM;
567 if (li->owner != NCP_DEFAULT_OWNER) {
568 owner = crget();
569 owner->cr_uid = li->owner;
570 } else {
571 owner = crhold(cred);
573 error = ncp_conn_alloc(td, owner, &conn);
574 if (error)
575 return (error);
576 if (error) {
577 ncp_conn_free(conn);
578 return error;
580 conn->li = *li;
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);
589 if (error) {
590 ncp_disconnect(conn);
591 } else {
592 *aconn=conn;
594 return error;
597 * Break connection and deallocate memory
600 ncp_disconnect(struct ncp_conn *conn) {
602 if (ncp_conn_access(conn,conn->ucred,NCPM_WRITE))
603 return EACCES;
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);
610 ncp_conn_free(conn);
611 return 0;
614 void
615 ncp_check_rq(struct ncp_conn *conn){
616 return;
617 if (conn->flags & NCPFL_INTR) return;
618 /* first, check for signals */
619 if (ncp_chkintr(conn,conn->td)) {
620 conn->flags |= NCPFL_INTR;
622 return;