5252e3fd8da93d7ffb2ccf04c20c3e5612375915
[dragonfly.git] / sys / netproto / ncp / ncp_ncp.c
1 /*
2  * Copyright (c) 1999, Boris Popov
3  * All rights reserved.
4  *
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.
19  *
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.
31  *
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.14 2007/04/20 05:42:23 dillon Exp $
34  *
35  * Core of NCP protocol
36  */
37 #include "opt_inet.h"
38 #include "opt_ipx.h"
39
40 #include <sys/param.h>
41 #include <sys/errno.h>
42 #include <sys/systm.h>
43 #include <sys/proc.h>
44 #include <sys/poll.h>
45 #include <sys/signalvar.h>
46 #include <sys/signal2.h>
47 #include <sys/mbuf.h>
48 #include <sys/thread2.h>
49 #include <sys/socketvar.h>
50
51 #ifdef IPX
52 #include <netproto/ipx/ipx.h>
53 #include <netproto/ipx/ipx_var.h>
54 #endif
55
56 #include "ncp.h"
57 #include "ncp_conn.h"
58 #include "ncp_sock.h"
59 #include "ncp_subr.h"
60 #include "ncp_ncp.h"
61 #include "ncp_rq.h"
62 #include "nwerror.h"
63
64 static int ncp_do_request(struct ncp_conn *,struct ncp_rq *rqp);
65 static int ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target);
66 static int ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options);
67 static void ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size);
68
69
70 #ifdef NCP_DATA_DEBUG
71 static void
72 m_dumpm(struct mbuf *m)
73 {
74         char *p;
75         int len;
76         kprintf("d=");
77         while(m) {
78                 p=mtod(m,char *);
79                 len=m->m_len;
80                 kprintf("(%d)",len);
81                 while(len--){
82                         kprintf("%02x ",((int)*(p++)) & 0xff);
83                 }
84                 m=m->m_next;
85         };
86         kprintf("\n");
87 }
88 #endif /* NCP_DATA_DEBUG */
89
90 int
91 ncp_chkintr(struct ncp_conn *conn, struct thread *td)
92 {
93         sigset_t tmpset;
94         struct lwp *lp = td->td_lwp;
95         struct proc *p = td->td_proc;
96
97         if (p == NULL)
98                 return 0;
99         tmpset = lwp_sigpend(lp);
100         SIGSETNAND(tmpset, lp->lwp_sigmask);
101         SIGSETNAND(tmpset, p->p_sigignore);
102         if (SIGNOTEMPTY(tmpset) && NCP_SIGMASK(tmpset))
103                 return EINTR;
104         return 0;
105 }
106
107 /*
108  * Process initial NCP handshake (attach)
109  * NOTE: Since all functions below may change conn attributes, they
110  * should be called with LOCKED connection, also they use td & ucred
111  */
112 int
113 ncp_ncp_connect(struct ncp_conn *conn) {
114         int error;
115         struct ncp_rphdr *rp;
116         DECLARE_RQ;
117         
118         conn->flags &= ~(NCPFL_INVALID | NCPFL_SIGNACTIVE | NCPFL_SIGNWANTED);
119         conn->seq = 0;
120         checkbad(ncp_rq_head(rqp,NCP_ALLOC_SLOT,0,conn->td,conn->ucred));
121         error=ncp_do_request(conn,rqp);
122         if (!error) {
123                 rp = mtod(rqp->rp, struct ncp_rphdr*);
124                 conn->connid = rp->conn_low + (rp->conn_high << 8);
125         }
126         ncp_rq_done(rqp);
127         if (error) return error;
128         conn->flags |= NCPFL_ATTACHED;
129
130         error = ncp_renegotiate_connparam(conn, NCP_DEFAULT_BUFSIZE, 0);
131         if (error == NWE_SIGNATURE_LEVEL_CONFLICT) {
132                 kprintf("Unable to negotiate requested security level\n");
133                 error = EOPNOTSUPP;
134         }
135         if (error) {
136                 ncp_ncp_disconnect(conn);
137                 return error;
138         }
139 #ifdef NCPBURST
140         ncp_burst_connect(conn);
141 #endif
142 bad:
143         return error;
144 }
145
146 int
147 ncp_ncp_disconnect(struct ncp_conn *conn) {
148         int error;
149         struct ncp_rqhdr *ncprq;
150         DECLARE_RQ;
151
152         NCPSDEBUG("for connid=%d\n",conn->nc_id);
153 #ifdef NCPBURST
154         ncp_burst_disconnect(conn);
155 #endif
156         error=ncp_rq_head(rqp,NCP_FREE_SLOT,0,conn->td,conn->ucred);
157         ncprq = mtod(rqp->rq,struct ncp_rqhdr*);
158         error=ncp_do_request(conn,rqp);
159         ncp_rq_done(rqp);
160         ncp_conn_invalidate(conn);
161         ncp_sock_disconnect(conn);
162         return 0;
163 }
164 /* 
165  * Make a signature for the current packet and add it at the end of the
166  * packet.
167  */
168 static void
169 ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size) {
170         u_char data[64];
171
172         bzero(data, sizeof(data));
173         bcopy(conn->sign_root, data, 8);
174         setdle(data, 8, *size);
175         m_copydata(rqp->rq, sizeof(struct ncp_rqhdr)-1,
176                 min((*size) - sizeof(struct ncp_rqhdr)+1, 52),data+12);
177         ncp_sign(conn->sign_state, data, conn->sign_state);
178         ncp_rq_mem(rqp, (void*)conn->sign_state, 8);
179         (*size) += 8;
180 }
181
182 /*
183  * Low level send rpc, here we do not attempt to restore any connection,
184  * Connection expected to be locked
185  */
186 static int 
187 ncp_do_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
188         int error=EIO,len, dosend, plen = 0, gotpacket;
189         struct socket *so;
190         struct thread *td = conn->td;
191         struct ncp_rqhdr *rq;
192         struct ncp_rphdr *rp=NULL;
193         struct timeval tv;
194         struct mbuf *m, *mreply = NULL;
195         
196         conn->nc_rq = rqp;
197         rqp->conn = conn;
198         if (td == NULL)
199                 td = curthread; /* XXX maybe procpage ? */
200         if (!ncp_conn_valid(conn)) {
201                 kprintf("%s: conn not valid\n",__func__);
202                 return (error);
203         }
204         so = conn->ncp_so;
205         if (!so) {
206                 kprintf("%s: ncp_so is NULL !\n",__func__);
207                 ncp_conn_invalidate(conn);      /* wow ! how we do that ? */
208                 return EBADF;
209         }
210         /*
211          * Flush out replies on previous reqs
212          */
213         crit_enter();
214         while (1/*so->so_rcv.sb_cc*/) {
215                 struct sorecv_direct sio;
216
217                 if (ncp_poll(so, POLLIN) == 0)
218                         break;
219                 if (ncp_sock_recv(so, &sio) != 0)       
220                         break;
221                 m_freem(sio.m0);
222         }
223         rq = mtod(rqp->rq,struct ncp_rqhdr *);
224         rq->seq = conn->seq;
225         m = rqp->rq;
226         len = 0;
227         while (m) {
228                 len += m->m_len;
229                 m = m->m_next;
230         }
231         rqp->rq->m_pkthdr.len = len;
232         switch(rq->fn) {
233             case 0x15: case 0x16: case 0x17: case 0x23:
234                 m = rqp->rq;
235                 *((u_int16_t*)(mtod(m,u_int8_t*)+sizeof(*rq))) = htons(len-2-sizeof(*rq));
236                 break;
237         }
238         if (conn->flags & NCPFL_SIGNACTIVE) {
239                 ncp_sign_packet(conn, rqp, &len);
240                 rqp->rq->m_pkthdr.len = len;
241         }
242         rq->conn_low = conn->connid & 0xff;
243         /* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/
244         /* XXX: this is temporary fix till I find a better solution */
245         rq->task = rq->conn_low;
246         rq->conn_high = conn->connid >> 8;
247         rqp->rexmit = conn->li.retry_count;
248         for(dosend = 1;;) {
249                 if (rqp->rexmit-- == 0) {
250                         error = ETIMEDOUT;
251                         break;
252                 }
253                 error = 0;
254                 if (dosend) {
255                         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,
256                                 rqp->rq->m_pkthdr.len, rq->seq, rq->task
257                         );
258                         error = ncp_sock_send(so, rqp->rq, rqp);
259                         if (error) break;
260                 }
261                 tv.tv_sec = conn->li.timeout;
262                 tv.tv_usec = 0;
263                 error = ncp_sock_rselect(so, td, &tv, POLLIN);
264                 if (error == EWOULDBLOCK )      /* timeout expired */
265                         continue;
266                 error = ncp_chkintr(conn, td);
267                 if (error == EINTR)             /* we dont restart */
268                         break;
269                 if (error) break;
270                 /*
271                  * At this point it is possible to get more than one
272                  * reply from server. In general, last reply should be for
273                  * current request, but not always. So, we loop through
274                  * all replies to find the right answer and flush others.
275                  */
276                 gotpacket = 0;  /* nothing good found */
277                 dosend = 1;     /* resend rq if error */
278                 for (;;) {
279                         struct sorecv_direct sio;
280
281                         error = 0;
282                         if (ncp_poll(so,POLLIN) == 0) break;
283                         error = ncp_sock_recv(so, &sio);
284                         if (error) break;               /* must be more checks !!! */
285
286                         m = sio.m0;
287                         if (m->m_len < sizeof(*rp)) {
288                                 m = m_pullup(m, sizeof(*rp));
289                                 if (m == NULL) {
290                                         kprintf("%s: reply too short\n",__func__);
291                                         continue;
292                                 }
293                         }
294                         rp = mtod(m, struct ncp_rphdr*);
295                         if (sio.len == sizeof(*rp) && rp->type == NCP_POSITIVE_ACK) {
296                                 NCPSDEBUG("got positive acknowledge\n");
297                                 m_freem(m);
298                                 rqp->rexmit = conn->li.retry_count;
299                                 dosend = 0;     /* server just busy and will reply ASAP */
300                                 continue;
301                         }
302                         NCPSDEBUG("recv:%04x c=%d l=%d s=%d t=%d cc=%02x cs=%02x\n",rp->type,
303                             (rp->conn_high << 8) + rp->conn_low, sio.len, rp->seq, rp->task,
304                              rp->completion_code, rp->connection_state);
305                         NCPDDEBUG(m);
306                         if ( (rp->type == NCP_REPLY) && 
307                             ((rq->type == NCP_ALLOC_SLOT) || 
308                             ((rp->conn_low == rq->conn_low) &&
309                              (rp->conn_high == rq->conn_high)
310                             ))) {
311                                 if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) {
312                                         dosend = 1;
313                                 }
314                                 if (rp->seq == rq->seq) {
315                                         if (gotpacket) {
316                                                 m_freem(m);
317                                         } else {
318                                                 gotpacket = 1;
319                                                 mreply = m;
320                                                 plen = sio.len;
321                                         }
322                                         continue;       /* look up other for other packets */
323                                 }
324                         }
325                         m_freem(m);
326                         NCPSDEBUG("reply mismatch\n");
327                 } /* for receive */
328                 if (error) break;
329                 if (gotpacket) break;
330                 /* try to resend, or just wait */
331         }
332         crit_exit();
333         conn->seq++;
334         if (error) {
335                 NCPSDEBUG("error=%d\n",error);
336                 if (error != EINTR)                     /* if not just interrupt */
337                         ncp_conn_invalidate(conn);      /* only reconnect to restore */
338                 return(error);
339         }
340         if (conn->flags & NCPFL_SIGNACTIVE) {
341                 /* XXX: check reply signature */
342                 m_adj(mreply, -8);
343                 plen -= 8;
344         }
345         len = plen;
346         m = mreply;
347         rp = mtod(m, struct ncp_rphdr*);
348         len -= sizeof(*rp);
349         rqp->rpsize = len;
350         rqp->cc = error = rp->completion_code;
351         if (error) error |= 0x8900;     /* server error */
352         rqp->cs = rp->connection_state;
353         if (rqp->cs & (NCP_CS_BAD_CONN | NCP_CS_SERVER_DOWN)) {
354                 NCPSDEBUG("server drop us\n");
355                 ncp_conn_invalidate(conn);
356                 error = ECONNRESET;
357         }
358         rqp->rp = m;
359         rqp->mrp = m;
360         rqp->bpos = mtod(m, caddr_t) + sizeof(*rp);
361         return error;
362 }
363
364 /*
365  * Here we will try to restore any loggedin & dropped connection,
366  * connection should be locked on entry
367  */
368 int ncp_restore_login(struct ncp_conn *conn);
369 int
370 ncp_restore_login(struct ncp_conn *conn) {
371         int error, oldflags;
372
373         if (conn->flags & NCPFL_RESTORING) {
374                 kprintf("Hey, ncp_restore_login called twise !!!\n");
375                 return 0;
376         }
377         oldflags = conn->flags;
378         kprintf("Restoring connection, flags = %d\n",oldflags);
379         if ((oldflags & NCPFL_LOGGED) == 0) {
380                 return ECONNRESET;      /* no need to restore empty conn */
381         }
382         conn->flags &= ~(NCPFL_LOGGED | NCPFL_ATTACHED);
383         conn->flags |= NCPFL_RESTORING;
384         do {    /* not a loop */
385                 error = ncp_reconnect(conn);
386                 if (error) break;
387                 if (conn->li.user)
388                         error = ncp_login_object(conn, conn->li.user, conn->li.objtype, conn->li.password,conn->td,conn->ucred);
389                 if (error) break;
390                 conn->flags |= NCPFL_LOGGED;
391         } while(0);
392         if (error) {
393                 conn->flags = oldflags | NCPFL_INVALID;
394         }
395         conn->flags &= ~NCPFL_RESTORING;
396         return error;
397 }
398
399 int
400 ncp_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
401         int error, rcnt;
402 /*      struct ncp_rqhdr *rq = mtod(rqp->rq,struct ncp_rqhdr*);*/
403
404         error = ncp_conn_lock(conn,rqp->td,rqp->cred,NCPM_EXECUTE);
405         if  (error) return error;
406         rcnt = NCP_RESTORE_COUNT;
407         for(;;) {
408                 if (!ncp_conn_valid(conn)) {
409                         if (rcnt==0) {
410                                 error = ECONNRESET;
411                                 break;
412                         }
413                         rcnt--;
414                         error = ncp_restore_login(conn);
415                         if (error)
416                                 continue;
417                 }
418                 error=ncp_do_request(conn, rqp);
419                 if (ncp_conn_valid(conn))       /* not just error ! */
420                         break;
421         }
422         ncp_conn_unlock(conn,rqp->td);
423         return error;
424 }
425
426 /*
427  * All negotiation functions expect a locked connection
428  */
429 static int
430 ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target) {
431         int error;
432         DECLARE_RQ;
433
434         NCP_RQ_HEAD(0x21,conn->td,conn->ucred);
435         ncp_rq_word_hl(rqp, size);
436         checkbad(ncp_request(conn,rqp));
437         *target = min(ncp_rp_word_hl(rqp), size);
438         NCP_RQ_EXIT;
439         return error;
440 }
441
442 static int
443 ncp_negotiate_size_and_options(struct ncp_conn *conn, int size, int options, 
444             int *ret_size, int *ret_options) {
445         int error;
446         int rs;
447         DECLARE_RQ;
448
449         NCP_RQ_HEAD(0x61,conn->td,conn->ucred);
450         ncp_rq_word_hl(rqp, size);
451         ncp_rq_byte(rqp, options);
452         checkbad(ncp_request(conn, rqp));
453         rs = ncp_rp_word_hl(rqp);
454         *ret_size = (rs == 0) ? size : min(rs, size);
455         ncp_rp_word_hl(rqp);    /* skip echo socket */
456         *ret_options = ncp_rp_byte(rqp);
457         NCP_RQ_EXIT;
458         return error;
459 }
460
461 static int
462 ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options)
463 {
464         int neg_buffsize, error, options, sl;
465
466         sl = conn->li.sig_level;
467         if (sl >= 2)
468                 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
469 #ifdef IPX
470         if (ipxcksum == 2)
471                 in_options |= NCP_IPX_CHECKSUM;
472 #endif
473         error = ncp_negotiate_size_and_options(conn, buffsize, in_options, 
474             &neg_buffsize, &options);
475         if (!error) {
476 #ifdef IPX
477                 if ((options ^ in_options) & NCP_IPX_CHECKSUM) {
478                         if (ipxcksum == 2) {
479                                 kprintf("Server refuses to support IPX checksums\n");
480                                 return NWE_REQUESTER_FAILURE;
481                         }
482                         in_options |= NCP_IPX_CHECKSUM;
483                         error = 1;
484                 }
485 #endif /* IPX */
486                 if ((options ^ in_options) & 2) {
487                         if (sl == 0 || sl == 3)
488                                 return NWE_SIGNATURE_LEVEL_CONFLICT;
489                         if (sl == 1) {
490                                 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
491                                 error = 1;
492                         }
493                 }
494                 if (error) {
495                         error = ncp_negotiate_size_and_options(conn,
496                             buffsize, in_options, &neg_buffsize, &options);
497                         if ((options ^ in_options) & 3) {
498                                 return NWE_SIGNATURE_LEVEL_CONFLICT;
499                         }
500                 }
501         } else {
502                 in_options &= ~NCP_SECURITY_LEVEL_SIGN_HEADERS;
503                 error = ncp_negotiate_buffersize(conn, NCP_DEFAULT_BUFSIZE,
504                               &neg_buffsize);
505         }                         
506         if (error) return error;
507         if ((neg_buffsize < 512) || (neg_buffsize > NCP_MAX_BUFSIZE))
508                 return EINVAL;
509         conn->buffer_size = neg_buffsize;
510         if (in_options & NCP_SECURITY_LEVEL_SIGN_HEADERS)
511                 conn->flags |= NCPFL_SIGNWANTED;
512 #ifdef IPX
513         ncp_sock_checksum(conn, in_options & NCP_IPX_CHECKSUM);
514 #endif
515         return 0;
516 }
517
518 int
519 ncp_reconnect(struct ncp_conn *conn) {
520         int error;
521
522         /* close any open sockets */
523         ncp_sock_disconnect(conn);
524         switch( conn->li.saddr.sa_family ) {
525 #ifdef IPX
526             case AF_IPX:
527                 error = ncp_sock_connect_ipx(conn);
528                 break;
529 #endif
530 #ifdef INET
531             case AF_INET:
532                 error = ncp_sock_connect_in(conn);
533                 break;
534 #endif
535             default:
536                 return EPROTONOSUPPORT;
537         }
538         if (!error)
539                 error = ncp_ncp_connect(conn);
540         return error;
541 }
542
543 /*
544  * Create conn structure and try to do low level connect
545  * Server addr should be filled in.
546  */
547 int
548 ncp_connect(struct ncp_conn_args *li, struct thread *td, struct ucred *cred,
549         struct ncp_conn **aconn)
550 {
551         struct ncp_conn *conn;
552         struct ucred *owner;
553         int error, isroot;
554
555         if (li->saddr.sa_family != AF_INET && li->saddr.sa_family != AF_IPX)
556                 return EPROTONOSUPPORT;
557         isroot = ncp_suser(cred) == 0;
558         /*
559          * Only root can change ownership
560          */
561         if (li->owner != NCP_DEFAULT_OWNER && !isroot)
562                 return EPERM;
563         if (li->group != NCP_DEFAULT_GROUP &&
564             !groupmember(li->group, cred) && !isroot)
565                 return EPERM;
566         if (li->owner != NCP_DEFAULT_OWNER) {
567                 owner = crget();
568                 owner->cr_uid = li->owner;
569         } else {
570                 owner = crhold(cred);
571         }
572         error = ncp_conn_alloc(td, owner, &conn);
573         if (error)
574                 return (error);
575         if (error) {
576                 ncp_conn_free(conn);
577                 return error;
578         }
579         conn->li = *li;
580         conn->nc_group = (li->group != NCP_DEFAULT_GROUP) ? 
581                 li->group : cred->cr_groups[0];
582
583         if (li->retry_count == 0)
584                 conn->li.retry_count = NCP_RETRY_COUNT;
585         if (li->timeout == 0)
586                 conn->li.timeout = NCP_RETRY_TIMEOUT;
587         error = ncp_reconnect(conn);
588         if (error) {
589                 ncp_disconnect(conn);
590         } else {
591                 *aconn=conn;
592         }
593         return error;
594 }
595 /*
596  * Break connection and deallocate memory
597  */
598 int
599 ncp_disconnect(struct ncp_conn *conn) {
600
601         if (ncp_conn_access(conn,conn->ucred,NCPM_WRITE))
602                 return EACCES;
603         if (conn->ref_cnt != 0) return EBUSY;
604         if (conn->flags & NCPFL_PERMANENT) return EBUSY;
605         if (ncp_conn_valid(conn)) {
606                 ncp_ncp_disconnect(conn);
607         }
608         ncp_sock_disconnect(conn);
609         ncp_conn_free(conn);
610         return 0;
611 }
612
613 void
614 ncp_check_rq(struct ncp_conn *conn){
615         return;
616         if (conn->flags & NCPFL_INTR) return;
617         /* first, check for signals */
618         if (ncp_chkintr(conn,conn->td)) {
619                 conn->flags |= NCPFL_INTR;
620         }
621         return;
622 }