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