Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[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.2 2003/06/17 04:28:53 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/mbuf.h>
47
48 #ifdef IPX
49 #include <netipx/ipx.h>
50 #include <netipx/ipx_var.h>
51 #endif
52
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>
60
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);
65
66
67 #ifdef NCP_DATA_DEBUG
68 static
69 void m_dumpm(struct mbuf *m) {
70         char *p;
71         int len;
72         printf("d=");
73         while(m) {
74                 p=mtod(m,char *);
75                 len=m->m_len;
76                 printf("(%d)",len);
77                 while(len--){
78                         printf("%02x ",((int)*(p++)) & 0xff);
79                 }
80                 m=m->m_next;
81         };
82         printf("\n");
83 }
84 #endif /* NCP_DATA_DEBUG */
85
86 int
87 ncp_chkintr(struct ncp_conn *conn, struct proc *p)
88 {
89         sigset_t tmpset;
90
91         if (p == NULL)
92                 return 0;
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))
97                 return EINTR;
98         return 0;
99 }
100
101 /*
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
105  */
106 int
107 ncp_ncp_connect(struct ncp_conn *conn) {
108         int error;
109         struct ncp_rphdr *rp;
110         DECLARE_RQ;
111         
112         conn->flags &= ~(NCPFL_INVALID | NCPFL_SIGNACTIVE | NCPFL_SIGNWANTED);
113         conn->seq = 0;
114         checkbad(ncp_rq_head(rqp,NCP_ALLOC_SLOT,0,conn->procp,conn->ucred));
115         error=ncp_do_request(conn,rqp);
116         if (!error) {
117                 rp = mtod(rqp->rp, struct ncp_rphdr*);
118                 conn->connid = rp->conn_low + (rp->conn_high << 8);
119         }
120         ncp_rq_done(rqp);
121         if (error) return error;
122         conn->flags |= NCPFL_ATTACHED;
123
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");
127                 error = EOPNOTSUPP;
128         }
129         if (error) {
130                 ncp_ncp_disconnect(conn);
131                 return error;
132         }
133 #ifdef NCPBURST
134         ncp_burst_connect(conn);
135 #endif
136 bad:
137         return error;
138 }
139
140 int
141 ncp_ncp_disconnect(struct ncp_conn *conn) {
142         int error;
143         struct ncp_rqhdr *ncprq;
144         DECLARE_RQ;
145
146         NCPSDEBUG("for connid=%d\n",conn->nc_id);
147 #ifdef NCPBURST
148         ncp_burst_disconnect(conn);
149 #endif
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);
153         ncp_rq_done(rqp);
154         ncp_conn_invalidate(conn);
155         ncp_sock_disconnect(conn);
156         return 0;
157 }
158 /* 
159  * Make a signature for the current packet and add it at the end of the
160  * packet.
161  */
162 static void
163 ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size) {
164         u_char data[64];
165
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);
173         (*size) += 8;
174 }
175
176 /*
177  * Low level send rpc, here we do not attempt to restore any connection,
178  * Connection expected to be locked
179  */
180 static int 
181 ncp_do_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
182         int error=EIO,len, dosend, plen = 0, gotpacket, s;
183         struct socket *so;
184         struct proc *p = conn->procp;
185         struct ncp_rqhdr *rq;
186         struct ncp_rphdr *rp=NULL;
187         struct timeval tv;
188         struct mbuf *m, *mreply = NULL;
189         
190         conn->nc_rq = rqp;
191         rqp->conn = conn;
192         if (p == NULL)
193                 p = curproc;    /* XXX maybe procpage ? */
194         if (!ncp_conn_valid(conn)) {
195                 printf("%s: conn not valid\n",__FUNCTION__);
196                 return (error);
197         }
198         so = conn->ncp_so;
199         if (!so) {
200                 printf("%s: ncp_so is NULL !\n",__FUNCTION__);
201                 ncp_conn_invalidate(conn);      /* wow ! how we do that ? */
202                 return EBADF;
203         }
204         /*
205          * Flush out replies on previous reqs
206          */
207         s = splnet();
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;
211                 m_freem(m);
212         }
213         rq = mtod(rqp->rq,struct ncp_rqhdr *);
214         rq->seq = conn->seq;
215         m = rqp->rq;
216         len = 0;
217         while (m) {
218                 len += m->m_len;
219                 m = m->m_next;
220         }
221         rqp->rq->m_pkthdr.len = len;
222         switch(rq->fn) {
223             case 0x15: case 0x16: case 0x17: case 0x23:
224                 m = rqp->rq;
225                 *((u_int16_t*)(mtod(m,u_int8_t*)+sizeof(*rq))) = htons(len-2-sizeof(*rq));
226                 break;
227         }
228         if (conn->flags & NCPFL_SIGNACTIVE) {
229                 ncp_sign_packet(conn, rqp, &len);
230                 rqp->rq->m_pkthdr.len = len;
231         }
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;
238         for(dosend = 1;;) {
239                 if (rqp->rexmit-- == 0) {
240                         error = ETIMEDOUT;
241                         break;
242                 }
243                 error = 0;
244                 if (dosend) {
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
247                         );
248                         error = ncp_sock_send(so, rqp->rq, rqp);
249                         if (error) break;
250                 }
251                 tv.tv_sec = conn->li.timeout;
252                 tv.tv_usec = 0;
253                 error = ncp_sock_rselect(so, p, &tv, POLLIN);
254                 if (error == EWOULDBLOCK )      /* timeout expired */
255                         continue;
256                 error = ncp_chkintr(conn, p);
257                 if (error == EINTR)             /* we dont restart */
258                         break;
259                 if (error) break;
260                 /*
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.
265                  */
266                 gotpacket = 0;  /* nothing good found */
267                 dosend = 1;     /* resend rq if error */
268                 for (;;) {
269                         error = 0;
270                         if (ncp_poll(so,POLLIN) == 0) break;
271 /*                      if (so->so_rcv.sb_cc == 0) {
272                                 break;
273                         }*/
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));
278                                 if (m == NULL) {
279                                         printf("%s: reply too short\n",__FUNCTION__);
280                                         continue;
281                                 }
282                         }
283                         rp = mtod(m, struct ncp_rphdr*);
284                         if (len == sizeof(*rp) && rp->type == NCP_POSITIVE_ACK) {
285                                 NCPSDEBUG("got positive acknowledge\n");
286                                 m_freem(m);
287                                 rqp->rexmit = conn->li.retry_count;
288                                 dosend = 0;     /* server just busy and will reply ASAP */
289                                 continue;
290                         }
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);
294                         NCPDDEBUG(m);
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)
299                             ))) {
300                                 if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) {
301                                         dosend = 1;
302                                 }
303                                 if (rp->seq == rq->seq) {
304                                         if (gotpacket) {
305                                                 m_freem(m);
306                                         } else {
307                                                 gotpacket = 1;
308                                                 mreply = m;
309                                                 plen = len;
310                                         }
311                                         continue;       /* look up other for other packets */
312                                 }
313                         }
314                         m_freem(m);
315                         NCPSDEBUG("reply mismatch\n");
316                 } /* for receive */
317                 if (error) break;
318                 if (gotpacket) break;
319                 /* try to resend, or just wait */
320         }
321         splx(s);
322         conn->seq++;
323         if (error) {
324                 NCPSDEBUG("error=%d\n",error);
325                 if (error != EINTR)                     /* if not just interrupt */
326                         ncp_conn_invalidate(conn);      /* only reconnect to restore */
327                 return(error);
328         }
329         if (conn->flags & NCPFL_SIGNACTIVE) {
330                 /* XXX: check reply signature */
331                 m_adj(mreply, -8);
332                 plen -= 8;
333         }
334         len = plen;
335         m = mreply;
336         rp = mtod(m, struct ncp_rphdr*);
337         len -= sizeof(*rp);
338         rqp->rpsize = len;
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);
345                 error = ECONNRESET;
346         }
347         rqp->rp = m;
348         rqp->mrp = m;
349         rqp->bpos = mtod(m, caddr_t) + sizeof(*rp);
350         return error;
351 }
352
353 /*
354  * Here we will try to restore any loggedin & dropped connection,
355  * connection should be locked on entry
356  */
357 int ncp_restore_login(struct ncp_conn *conn);
358 int
359 ncp_restore_login(struct ncp_conn *conn) {
360         int error, oldflags;
361
362         if (conn->flags & NCPFL_RESTORING) {
363                 printf("Hey, ncp_restore_login called twise !!!\n");
364                 return 0;
365         }
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 */
370         }
371         conn->flags &= ~(NCPFL_LOGGED | NCPFL_ATTACHED);
372         conn->flags |= NCPFL_RESTORING;
373         do {    /* not a loop */
374                 error = ncp_reconnect(conn);
375                 if (error) break;
376                 if (conn->li.user)
377                         error = ncp_login_object(conn, conn->li.user, conn->li.objtype, conn->li.password,conn->procp,conn->ucred);
378                 if (error) break;
379                 conn->flags |= NCPFL_LOGGED;
380         } while(0);
381         if (error) {
382                 conn->flags = oldflags | NCPFL_INVALID;
383         }
384         conn->flags &= ~NCPFL_RESTORING;
385         return error;
386 }
387
388 int
389 ncp_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
390         int error, rcnt;
391 /*      struct ncp_rqhdr *rq = mtod(rqp->rq,struct ncp_rqhdr*);*/
392
393         error = ncp_conn_lock(conn,rqp->p,rqp->cred,NCPM_EXECUTE);
394         if  (error) return error;
395         rcnt = NCP_RESTORE_COUNT;
396         for(;;) {
397                 if (!ncp_conn_valid(conn)) {
398                         if (rcnt==0) {
399                                 error = ECONNRESET;
400                                 break;
401                         }
402                         rcnt--;
403                         error = ncp_restore_login(conn);
404                         if (error)
405                                 continue;
406                 }
407                 error=ncp_do_request(conn, rqp);
408                 if (ncp_conn_valid(conn))       /* not just error ! */
409                         break;
410         }
411         ncp_conn_unlock(conn,rqp->p);
412         return error;
413 }
414
415 /*
416  * All negotiation functions expect a locked connection
417  */
418 static int
419 ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target) {
420         int error;
421         DECLARE_RQ;
422
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);
427         NCP_RQ_EXIT;
428         return error;
429 }
430
431 static int
432 ncp_negotiate_size_and_options(struct ncp_conn *conn, int size, int options, 
433             int *ret_size, int *ret_options) {
434         int error;
435         int rs;
436         DECLARE_RQ;
437
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);
446         NCP_RQ_EXIT;
447         return error;
448 }
449
450 static int
451 ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options)
452 {
453         int neg_buffsize, error, options, sl;
454
455         sl = conn->li.sig_level;
456         if (sl >= 2)
457                 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
458 #ifdef IPX
459         if (ipxcksum == 2)
460                 in_options |= NCP_IPX_CHECKSUM;
461 #endif
462         error = ncp_negotiate_size_and_options(conn, buffsize, in_options, 
463             &neg_buffsize, &options);
464         if (!error) {
465 #ifdef IPX
466                 if ((options ^ in_options) & NCP_IPX_CHECKSUM) {
467                         if (ipxcksum == 2) {
468                                 printf("Server refuses to support IPX checksums\n");
469                                 return NWE_REQUESTER_FAILURE;
470                         }
471                         in_options |= NCP_IPX_CHECKSUM;
472                         error = 1;
473                 }
474 #endif /* IPX */
475                 if ((options ^ in_options) & 2) {
476                         if (sl == 0 || sl == 3)
477                                 return NWE_SIGNATURE_LEVEL_CONFLICT;
478                         if (sl == 1) {
479                                 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
480                                 error = 1;
481                         }
482                 }
483                 if (error) {
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;
488                         }
489                 }
490         } else {
491                 in_options &= ~NCP_SECURITY_LEVEL_SIGN_HEADERS;
492                 error = ncp_negotiate_buffersize(conn, NCP_DEFAULT_BUFSIZE,
493                               &neg_buffsize);
494         }                         
495         if (error) return error;
496         if ((neg_buffsize < 512) || (neg_buffsize > NCP_MAX_BUFSIZE))
497                 return EINVAL;
498         conn->buffer_size = neg_buffsize;
499         if (in_options & NCP_SECURITY_LEVEL_SIGN_HEADERS)
500                 conn->flags |= NCPFL_SIGNWANTED;
501 #ifdef IPX
502         ncp_sock_checksum(conn, in_options & NCP_IPX_CHECKSUM);
503 #endif
504         return 0;
505 }
506
507 int
508 ncp_reconnect(struct ncp_conn *conn) {
509         int error;
510
511         /* close any open sockets */
512         ncp_sock_disconnect(conn);
513         switch( conn->li.saddr.sa_family ) {
514 #ifdef IPX
515             case AF_IPX:
516                 error = ncp_sock_connect_ipx(conn);
517                 break;
518 #endif
519 #ifdef INET
520             case AF_INET:
521                 error = ncp_sock_connect_in(conn);
522                 break;
523 #endif
524             default:
525                 return EPROTONOSUPPORT;
526         }
527         if (!error)
528                 error = ncp_ncp_connect(conn);
529         return error;
530 }
531
532 /*
533  * Create conn structure and try to do low level connect
534  * Server addr should be filled in.
535  */
536 int
537 ncp_connect(struct ncp_conn_args *li, struct proc *p, struct ucred *cred,
538         struct ncp_conn **aconn)
539 {
540         struct ncp_conn *conn;
541         struct ucred *owner;
542         int error, isroot;
543
544         if (li->saddr.sa_family != AF_INET && li->saddr.sa_family != AF_IPX)
545                 return EPROTONOSUPPORT;
546         isroot = ncp_suser(cred) == 0;
547         /*
548          * Only root can change ownership
549          */
550         if (li->owner != NCP_DEFAULT_OWNER && !isroot)
551                 return EPERM;
552         if (li->group != NCP_DEFAULT_GROUP &&
553             !groupmember(li->group, cred) && !isroot)
554                 return EPERM;
555         if (li->owner != NCP_DEFAULT_OWNER) {
556                 owner = crget();
557                 owner->cr_uid = li->owner;
558         } else {
559                 owner = cred;
560                 crhold(owner);
561         }
562         error = ncp_conn_alloc(p, owner, &conn);
563         if (error)
564                 return (error);
565         if (error) {
566                 ncp_conn_free(conn);
567                 return error;
568         }
569         conn->li = *li;
570         conn->nc_group = (li->group != NCP_DEFAULT_GROUP) ? 
571                 li->group : cred->cr_groups[0];
572
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);
578         if (error) {
579                 ncp_disconnect(conn);
580         } else {
581                 *aconn=conn;
582         }
583         return error;
584 }
585 /*
586  * Break connection and deallocate memory
587  */
588 int
589 ncp_disconnect(struct ncp_conn *conn) {
590
591         if (ncp_conn_access(conn,conn->ucred,NCPM_WRITE))
592                 return EACCES;
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);
597         }
598         ncp_sock_disconnect(conn);
599         ncp_conn_free(conn);
600         return 0;
601 }
602
603 void
604 ncp_check_rq(struct ncp_conn *conn){
605         return;
606         if (conn->flags & NCPFL_INTR) return;
607         /* first, check for signals */
608         if (ncp_chkintr(conn,conn->procp)) {
609                 conn->flags |= NCPFL_INTR;
610         }
611         return;
612 }